final case class Box[A](value: A) // type이 A라고 정해짐, A가 잘 정의되면 어떤 것이든 담을 수 있음
defined class Box
Box(0)
// 알아서 타입 추론을 해서 맞춰줌
res4: Box[Int] = Box(0)
Box("foo")
res5: Box[String] = Box("foo")
def generic[A](in: A): A = in
defined function generic
generic[String]("foo")
res7: String = "foo"
generic("foo")
res8: String = "foo"
generic(1)
res9: Int = 1
class
Trait
Method
sealed trait LinkedList[A]
final case class End[A]() extends LinkedList[A]
final case class Pair[A](head: A, tail: LinkedList[A]) extends LinkedList[A]
defined trait LinkedList defined class End defined class Pair
val intList = Pair(1, Pair(2, Pair(3, End())))
intList: Pair[Int] = Pair(1,Pair(2,Pair(3,End())))
val strList = Pair("a", Pair("b", Pair("c", End())))
strList: Pair[String] = Pair(a,Pair(b,Pair(c,End())))
Function TYPE
(parameter: type, ..) => expression
val sayHi = () => "Hi!"
// unit을 받아서 string을 토하는 함수
// lambda : 익명 함수
sayHi: () => String = <function0>
sayHi()
res21: String = "Hi!"
val add1 = (x: Int) => x + 1
add1: Int => Int = <function1>
add1(10)
res26: Int = 11
add1("a")
cmd27.sc:1: type mismatch; found : String("a") required: Int val res27 = add1("a") ^
Compilation Failed
val sum = (x: Int, y: Int) => x + y
sum: (Int, Int) => Int = <function2>
sum(1, 2)
res28: Int = 3
def oneAndTwo[A](f: (Int, Int) => A): A = f(1,2)
// 메서드에서 함수를 인자로 받음
defined function oneAndTwo
oneAndTwo((x, y) => x + y)
// 여기선 type을 안적어도 됨 (위에서 Int로 정의함)
res30: Int = 3
oneAndTwo((x, y) => s"($x, $y)")
res31: String = "(1, 2)"
(_: Int) + 1 // (x: Int) => x + 1
// x를 한번만 쓰이니까 굳이 쓸 필요가 뭐있나! 이런 느낌
res32: Int => Int = <function1>
res32(1)
res33: Int = 2
// 파라미터 타입을 추론할 수 있는 경우엔 타입을 생략할 수 있습니다
_ + _ // (a, b) => a + b
foo(_) // (a) => foo(a)
foo(_, b) // (a) => foo(a, b)
_(foo) // (a) => a(foo)
case class Pair[A, B](a: A, b: B) {
def calc[C](f: (A, B) => C): C = f(a, b)
}
defined class Pair
Pair(1, 2).calc(_+_)
// 길게 쓰면 아래와 같이 씀!
res35: Int = 3
val caseInt = Pair(1, 2).calc((a, b) => a+b)
caseInt: Int = 3
Pair("foo", "bar").calc(_+_)
res36: String = "foobar"
// 짧아지긴 하지만.. 아직 적응이 잘 안될거에요.. 계속 하다보면..!
object Adder {
def add1(n: Int): Int = n+1
}
defined object Adder
Adder.add1
// error 발생
cmd40.sc:1: missing argument list for method add1 in object Adder Unapplied methods are only converted to functions when a function type is expected. You can make this conversion explicit by writing `add1 _` or `add1(_)` instead of `add1`. val res40 = Adder.add1 ^
Compilation Failed
Adder.add1 _
// 함수로 바꿔줌
res40: Int => Int = <function1>
Adder.add1(_)
// 함수로 바꿔줌
res41: Int => Int = <function1>
case class Box[A](a: A) {
def modify[B](f: A => B): B = f(a)
}
defined class Box
Box(42).modify(Adder.add1)
res43: Int = 43
Box(42) modify Adder.add1
res44: Int = 43
Box("foo") modify Adder.add1
// add1은 Int를 받는 함수인데 foo는 string이라 error 발생
cmd45.sc:1: type mismatch; found : Int => Int required: String => ? val res45 = Box("foo") modify Adder.add1 ^
Compilation Failed
def curring(x: Int)(y: Int): Int = x + y
defined function curring
curring(1)(2)
res46: Int = 3
def generic[A, B](a: A, f: A => B): B = f(a)
defined function generic
generic(1, _ + 1)
// 왜 컴파일이 안될까요?
// 파라미터 타입이 없음..!
cmd48.sc:1: missing parameter type for expanded function ((x$1) => x$1.$plus(1)) val res48 = generic(1, _ + 1) ^
Compilation Failed
generic(1, a=> a + 1)
// 타입 추론은 괄호 단위로만 가능.
// a:A를 유추해 f:A를 쓸 수 없습니다. 명시적으로 적어줘야 함
cmd48.sc:1: missing parameter type val res48 = generic(1, a=> a + 1) ^
Compilation Failed
def genericCurring[A, B](a: A)(f: A => B): B = f(a)
defined function genericCurring
genericCurring(1)(_ + 1)
res50: Int = 2
val pair1 = Pair("foo", 1)
val pair2 = Pair(2, "bar")
assert(pair1.one == "foo")
assert(pair1.two == 1)
assert(pair2.one == 2)
assert(pair2.two == "bar")
println("ok")
ok
pair1: Pair[String, Int] = Pair("foo", 1) pair2: Pair[Int, String] = Pair(2, "bar")
case class Pair[A, B](one: A, two: B)
defined class Pair
Tuple2("foo", 1)
res54: (String, Int) = ("foo", 1)
("foo", 1)
res55: (String, Int) = ("foo", 1)
("foo", 1, true)
res56: (String, Int, Boolean) = ("foo", 1, true)
("foo", 1) match { case (a, b) => a + b}
res57: String = "foo1"
val x = (1, "b", true)
x: (Int, String, Boolean) = (1, "b", true)
x._1
res59: Int = 1
x._3
res60: Boolean = true
sealed trait Sum[A, B]
final case class First[A, B]() extends Sum[A, B]
final case class Second[A, B]() extends Sum[A, B]
defined trait Sum defined class First defined class Second
assert(First[Int, String](1).value == 1)
assert(Second[Int, String]("foo").value == "foo")
val sum: Sum[Int, String] = Second("foo")
val matched: String = sum match {
case First(x) => x.toString
case Second(x) => x
}
assert(matched == "foo")
println("ok")
cmd62.sc:1: too many arguments for method apply: ()cmd62Wrapper.this.cmd61.wrapper.First[Int,String] in object First val res62_0 = assert(First[Int, String](1).value == 1) ^cmd62.sc:2: too many arguments for method apply: ()cmd62Wrapper.this.cmd61.wrapper.Second[Int,String] in object Second val res62_1 = assert(Second[Int, String]("foo").value == "foo") ^cmd62.sc:3: too many arguments for method apply: ()cmd62Wrapper.this.cmd61.wrapper.Second[A,B] in object Second val sum: Sum[Int, String] = Second("foo") ^cmd62.sc:5: wrong number of arguments for pattern cmd62Wrapper.this.cmd61.wrapper.First[Int,String]() case First(x) => x.toString ^cmd62.sc:6: wrong number of arguments for pattern cmd62Wrapper.this.cmd61.wrapper.Second[Int,String]() case Second(x) => x ^
Compilation Failed
sealed trait Sum[A, B]
final case class First[A, B](value: A) extends Sum[A, B]
final case class Second[A, B](value: B) extends Sum[A, B]
defined trait Sum defined class First defined class Second
// 인자를 1개만 받음
// .value가 써있기 때문에 value를 넣어주면 됨
assert(First[Int, String](1).value == 1)
assert(Second[Int, String]("foo").value == "foo")
val sum: Sum[Int, String] = Second("foo")
val matched: String = sum match {
case First(x) => x.toString
case Second(x) => x
}
assert(matched == "foo")
println("ok")
ok
sum: Sum[Int, String] = Second(foo) matched: String = "foo"
def divide(a: Int, b: Int): Maybe[Int] = b match {
case 0 => Empty()
case _ => Just(a/b)
}
assert(divide(10, 2) == Just(5))
assert(divide(10, 0) == Empty())
cmd71.sc:6: too many arguments for method apply: ()cmd71Wrapper.this.cmd70.wrapper.Just[A] in object Just val res71_1 = assert(divide(10, 2) == Just(5)) ^cmd71.sc:3: too many arguments for method apply: ()cmd71Wrapper.this.cmd70.wrapper.Just[A] in object Just case _ => Just(a/b) ^
Compilation Failed
sealed trait Maybe[A]
final case class Empty[A]() extends Maybe[A]
final case class Just[A](value: A) extends Maybe[A]
defined trait Maybe defined class Empty defined class Just
def divide(a: Int, b: Int): Maybe[Int] = b match {
case 0 => Empty()
case _ => Just(a/b)
}
assert(divide(10, 2) == Just(5))
assert(divide(10, 0) == Empty())
defined function divide
divide(10,1)
res75: Maybe[Int] = Just(10)
앞서 작성했던 LinkedList에 map메소드를 구현해보세요.
타입 시그니쳐는 다음과 같습니다.
sealed trait LinkedList[A]{
def map[B](f: A => B): LinkedList[B] =
}
final case class End[A]() extends LinkedList[A]
final case class Pair[A](head: A, tail: LinkedList[A]) extends LinkedList[A]
val l = Pair(1, Pair(2, Pair(3, End())))
assert(l.map(_ + 1) == Pair(2, Pair(3, Pair(4, End()))))
println("ok")
cmd76.sc:2: value map is not a member of cmd76Wrapper.this.cmd51.wrapper.Pair[Int,cmd76Wrapper.this.cmd51.wrapper.Pair[Int,cmd76Wrapper.this.cmd51.wrapper.Pair[Int,cmd76Wrapper.this.cmd22.wrapper.End[Nothing]]]] val res76_1 = assert(l.map(_ + 1) == Pair(2, Pair(3, Pair(4, End())))) ^
Compilation Failed
sealed trait LinkedList[A]{
def map[B](f: A => B): LinkedList[B] = this match {
case End() => End[B]()
case Pair(head, tail) => Pair[B](f(head) , tail.map(f))
}
}
final case class End[A]() extends LinkedList[A]
final case class Pair[A](head: A, tail: LinkedList[A]) extends LinkedList[A]
defined trait LinkedList defined class End defined class Pair
val l = Pair(1, Pair(2, Pair(3, End())))
assert(l.map(_ + 1) == Pair(2, Pair(3, Pair(4, End()))))
println("ok")
ok
l: Pair[Int] = Pair(1,Pair(2,Pair(3,End())))
LinkedList에 fold메소드를 구현해보세요.
타입 시그니쳐는 다음과 같습니다
def fold[B](init: B)(f: (A, B) => B): B = ???
// test
val list0 = Pair(1, Pair(2, Pair(3, Pair(4, End))))
assert(list0.fold(0)(_ + _) == 10)
val list1 = Pair(1, Pair(2, Pair(3, Pair(4, End))))
assert(list1.fold(1)(_ * _) == 24)
sealed trait LinkedList[A]{
def fold[B](init: B)(f: (A, B) => B): B = this match{
case End() => init
case Pair(head, tail) => f(head, tail.fold(init)(f))
}
}
final case class End[A]() extends LinkedList[A]
final case class Pair[A](head: A, tail: LinkedList[A]) extends LinkedList[A]
defined trait LinkedList defined class End defined class Pair
// test
val list = Pair(1, Pair(2, Pair(3, Pair(4, End))))
val filtered = list.flatMap{
case n if x % 2 == 0 => End
case n => Pair(n, End)
}
assert(filtered == Pair(1, Pair(3, End)))
sealed trait LinkedList[A] {
def map[B](f: A => B): LinkedList[B] = this match {
case End() => End[B]()
case Pair(head, tail) => Pair[B](f(head), tail.map(f))
}
def fold[B](init: B)(f: (A, B) => B): B = this match {
case End() => init
case Pair(head, tail) => f(head, tail.fold(init)(f))
}
def flatMap[B](f: A => LinkedList[B]): LinkedList[B] = this match {
case End() => End()
case Pair(head, tail) =>
val headList: LinkedList[B] = f(head)
val tailList: LinkedList[B] = tail.flatMap(f)
headList ++ tailList
}
def ++(that: LinkedList[A]): LinkedList[A] = this match {
case End() => that
case Pair(head, tail) => Pair(head, tail ++ that)
}
}
final case class End[A]() extends LinkedList[A]
final case class Pair[A](head: A, tail: LinkedList[A]) extends LinkedList[A]
val list = Pair(1, Pair(2, Pair(3, Pair(4, End()))))
val filtered = list.flatMap[Int]{
case n if n % 2 == 0 => End()
case n => Pair(n, End())
}
assert(filtered == Pair(1, Pair(3, End())))
println("Ok!")
Ok!
defined trait LinkedList defined class End defined class Pair list: wrapper.wrapper.Pair[Int] = Pair(1,Pair(2,Pair(3,Pair(4,End())))) filtered: wrapper.wrapper.LinkedList[Int] = Pair(1,Pair(3,End()))
sealed trait Maybe[A]
final case class Just[A](value: A) extends Maybe[A]
final case class Empty[A]() extends Maybe[A]
defined trait Maybe defined class Just defined class Empty
// 인자가 없으니 아래처럼 만들면 간단한데!
sealed trait Maybe[A]
final case class Just[A](value: A) extends Maybe[A]
final case object Empty extends Maybe[???]
cmd81.sc:3: not found: type ??? final case object Empty extends Maybe[???] ^
Compilation Failed
sealed trait Maybe[A]
final case class Just[A](value: A) extends Maybe[A]
final case object Empty extends Maybe[Nothing]
// Nothing : 어떤 것들의 sub type!!!
defined trait Maybe defined class Just defined object Empty
// 명시적으로 해주는 것을 variance
val perhaps: Maybe[Int] = Empty
cmd82.sc:1: type mismatch; found : cmd82Wrapper.this.cmd81.wrapper.Empty.type required: cmd82Wrapper.this.cmd81.wrapper.Maybe[Int] Note: Nothing <: Int (and cmd82Wrapper.this.cmd81.wrapper.Empty.type <: cmd82Wrapper.this.cmd81.wrapper.Maybe[Nothing]), but trait Maybe is invariant in type A. You may wish to define A as +A instead. (SLS 4.5) val perhaps: Maybe[Int] = Empty ^
Compilation Failed
sealed trait Maybe[+A]
final case class Just[A](value: A) extends Maybe[A]
final case object Empty extends Maybe[Nothing]
defined trait Maybe defined class Just defined object Empty
trait Function0[+R] {
def apply: R
}
trait Function1[-A, +B] {
def apply(a: A): B
}
trait Function2[-A, -B, +C] {
def apply(a: A, b: B): C
}
case class Box[A](a: A) {
def map[B](f: Function1[A, B]): Box[B] = Box(f(a))
}
case class Box[+A](a: A) {
def set(a: A): Box[A] = Box(a) // 컴파일 안됨! 인자에만 들어갈 수 있음
}
cmd83.sc:2: covariant type A occurs in contravariant position in type A of value a def set(a: A): Box[A] = Box(a) // 컴파일 안됨! ^
Compilation Failed
case class Box[+A](a: A) {
def set[AA >: A](a: AA): Box[AA] = Box(a)
}
// AA라는 super type이 있을 경우 AA를 넣어서 Box type을 만든다^^.. 이해 안가죠?
sealed trait Sum[+A, +B] {
def flatMap[C](f: B => Sum[A, C]): Sum[A, C] = this match {
case Failure(v) => Failure(v)
case Success(v) => f(v)
}
}
final case class Failure[A](value: A) extends Sum[A, Nothing]
final case class Success[B](value: B) extends Sum[Nothing, B]
cmd83.sc:2: covariant type A occurs in contravariant position in type B => Helper.this.Sum[A,C] of value f def flatMap[C](f: B => Sum[A, C]): Sum[A, C] = this match { ^
Compilation Failed
sealed trait Sum[+A, +B] {
def flatMap[AA >: A, C](f: B => Sum[AA, C]): Sum[AA, C] = this match {
case Failure(v) => Failure(v)
case Success(v) => f(v)
}
}
final case class Failure[A](value: A) extends Sum[A, Nothing]
final case class Success[B](value: B) extends Sum[Nothing, B]
defined trait Sum defined class Failure defined class Success
sealed trait Expression {
def eval: Sum[String, Double] = ???
}
final case class Addition(left: Expression, right: Expression) extends Expression
final case class Subtraction(left: Expression, right: Expression) extends Expression
final case class Division(left: Expression, right: Expression) extends Expression
final case class SquareRoot(value: Expression) extends Expression
final case class Number(value: Double) extends Expression
// test
assert(Addition(Number(1), Number(2)).eval == Success(3))
assert(SquareRoot(Number(-1)).eval == Failure("Square root of negative number"))
assert(Division(Number(4), Number(0)).eval == Failure("Division by zero"))
assert(Division(Addition(Subtraction(Number(8), Number(6)), Number(2)), Number(2)).eval == Success(2.0))