{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "- 사이드 이펙트 : 함수형은 input, output 이런 것을 할 수 있는데 그 이외에 할 수 있는 것들 (중간에 쓰윽-하는 것들)\n", " - 안 쓸 수 있으면 안쓰는 것이 좋음. 컴파일러가 체크 불가능\n", " - 그러나 신경은 써야 한다!\n", " - 하스켈에서는 사이드 이펙트가 허용되지 않는 편!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Generic\n", "- 자바의 Generic과 의미가 다를 수 있음\n", "- Trait보다 더 일반화된 것" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "defined \u001b[32mclass\u001b[39m \u001b[36mBox\u001b[39m" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "final case class Box[A](value: A) // type이 A라고 정해짐, A가 잘 정의되면 어떤 것이든 담을 수 있음" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "\u001b[36mres4\u001b[39m: \u001b[32mBox\u001b[39m[\u001b[32mInt\u001b[39m] = \u001b[33mBox\u001b[39m(\u001b[32m0\u001b[39m)" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Box(0)\n", "// 알아서 타입 추론을 해서 맞춰줌" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "\u001b[36mres5\u001b[39m: \u001b[32mBox\u001b[39m[\u001b[32mString\u001b[39m] = \u001b[33mBox\u001b[39m(\u001b[32m\"foo\"\u001b[39m)" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Box(\"foo\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Generic Method" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "defined \u001b[32mfunction\u001b[39m \u001b[36mgeneric\u001b[39m" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def generic[A](in: A): A = in" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "\u001b[36mres7\u001b[39m: \u001b[32mString\u001b[39m = \u001b[32m\"foo\"\u001b[39m" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "generic[String](\"foo\")" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "\u001b[36mres8\u001b[39m: \u001b[32mString\u001b[39m = \u001b[32m\"foo\"\u001b[39m" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "generic(\"foo\")" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "\u001b[36mres9\u001b[39m: \u001b[32mInt\u001b[39m = \u001b[32m1\u001b[39m" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "generic(1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Syntax\n", "- class\n", " - (case) class Name\\[A, B, ...](...){....}\n", " - 자바는 T를 많이 쓰는데 스칼라에선 A, B를 씀\n", "\n", "- Trait\n", " - trait Name\\[A, B, ...]{..}\n", "\n", "- Method\n", " - def name\\[A,B ...](...){...}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## EXERCISE: GENERIC LIST\n", "\n", "- 다음 IntList를 Generic을 써서 일반적인 타입의 데이터를 담을 수 있도록 수정한 LinkedList 타입을 작성해 보세요.\n" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "defined \u001b[32mtrait\u001b[39m \u001b[36mLinkedList\u001b[39m\n", "defined \u001b[32mclass\u001b[39m \u001b[36mEnd\u001b[39m\n", "defined \u001b[32mclass\u001b[39m \u001b[36mPair\u001b[39m" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sealed trait LinkedList[A]\n", "final case class End[A]() extends LinkedList[A]\n", "final case class Pair[A](head: A, tail: LinkedList[A]) extends LinkedList[A]" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "\u001b[36mintList\u001b[39m: \u001b[32mPair\u001b[39m[\u001b[32mInt\u001b[39m] = Pair(1,Pair(2,Pair(3,End())))" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "val intList = Pair(1, Pair(2, Pair(3, End())))" ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "\u001b[36mstrList\u001b[39m: \u001b[32mPair\u001b[39m[\u001b[32mString\u001b[39m] = Pair(a,Pair(b,Pair(c,End())))" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "val strList = Pair(\"a\", Pair(\"b\", Pair(\"c\", End())))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Functions\n", "- Function TYPE\n", " - \\(A, B, ...) => C\n", " - 만약 파라미터가 한개라면\n", " - A => B\n", " \n", "- (parameter: type, ..) => expression" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "\u001b[36msayHi\u001b[39m: () => \u001b[32mString\u001b[39m = " ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "val sayHi = () => \"Hi!\"\n", "// unit을 받아서 string을 토하는 함수\n", "// lambda : 익명 함수" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "\u001b[36mres21\u001b[39m: \u001b[32mString\u001b[39m = \u001b[32m\"Hi!\"\u001b[39m" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sayHi()" ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "\u001b[36madd1\u001b[39m: \u001b[32mInt\u001b[39m => \u001b[32mInt\u001b[39m = " ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "val add1 = (x: Int) => x + 1" ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "\u001b[36mres26\u001b[39m: \u001b[32mInt\u001b[39m = \u001b[32m11\u001b[39m" ] }, "execution_count": 27, "metadata": {}, "output_type": "execute_result" } ], "source": [ "add1(10)" ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "cmd27.sc:1: type mismatch;\n", " found : String(\"a\")\n", " required: Int\n", "val res27 = add1(\"a\")\n", " ^" ] }, { "ename": "", "evalue": "", "output_type": "error", "traceback": [ "Compilation Failed" ] } ], "source": [ "add1(\"a\")" ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "\u001b[36msum\u001b[39m: (\u001b[32mInt\u001b[39m, \u001b[32mInt\u001b[39m) => \u001b[32mInt\u001b[39m = " ] }, "execution_count": 28, "metadata": {}, "output_type": "execute_result" } ], "source": [ "val sum = (x: Int, y: Int) => x + y" ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "\u001b[36mres28\u001b[39m: \u001b[32mInt\u001b[39m = \u001b[32m3\u001b[39m" ] }, "execution_count": 29, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sum(1, 2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- 함수형 프로그래밍에서는 method에서 함수를 인자로 받을 수 있음!!" ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "defined \u001b[32mfunction\u001b[39m \u001b[36moneAndTwo\u001b[39m" ] }, "execution_count": 30, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def oneAndTwo[A](f: (Int, Int) => A): A = f(1,2)\n", "// 메서드에서 함수를 인자로 받음" ] }, { "cell_type": "code", "execution_count": 31, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "\u001b[36mres30\u001b[39m: \u001b[32mInt\u001b[39m = \u001b[32m3\u001b[39m" ] }, "execution_count": 31, "metadata": {}, "output_type": "execute_result" } ], "source": [ "oneAndTwo((x, y) => x + y)\n", "// 여기선 type을 안적어도 됨 (위에서 Int로 정의함)" ] }, { "cell_type": "code", "execution_count": 32, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "\u001b[36mres31\u001b[39m: \u001b[32mString\u001b[39m = \u001b[32m\"(1, 2)\"\u001b[39m" ] }, "execution_count": 32, "metadata": {}, "output_type": "execute_result" } ], "source": [ "oneAndTwo((x, y) => s\"($x, $y)\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Placeholder syntax\n", "- 함수를 간단히 쓸 수 있는 Syntax" ] }, { "cell_type": "code", "execution_count": 33, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "\u001b[36mres32\u001b[39m: \u001b[32mInt\u001b[39m => \u001b[32mInt\u001b[39m = " ] }, "execution_count": 33, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(_: Int) + 1 // (x: Int) => x + 1\n", "// x를 한번만 쓰이니까 굳이 쓸 필요가 뭐있나! 이런 느낌" ] }, { "cell_type": "code", "execution_count": 34, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "\u001b[36mres33\u001b[39m: \u001b[32mInt\u001b[39m = \u001b[32m2\u001b[39m" ] }, "execution_count": 34, "metadata": {}, "output_type": "execute_result" } ], "source": [ "res32(1)" ] }, { "cell_type": "code", "execution_count": 34, "metadata": {}, "outputs": [], "source": [ "// 파라미터 타입을 추론할 수 있는 경우엔 타입을 생략할 수 있습니다\n", "\n", "_ + _ // (a, b) => a + b\n", "foo(_) // (a) => foo(a)\n", "foo(_, b) // (a) => foo(a, b)\n", "_(foo) // (a) => a(foo)" ] }, { "cell_type": "code", "execution_count": 35, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "defined \u001b[32mclass\u001b[39m \u001b[36mPair\u001b[39m" ] }, "execution_count": 35, "metadata": {}, "output_type": "execute_result" } ], "source": [ "case class Pair[A, B](a: A, b: B) {\n", " def calc[C](f: (A, B) => C): C = f(a, b)\n", "}" ] }, { "cell_type": "code", "execution_count": 36, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "\u001b[36mres35\u001b[39m: \u001b[32mInt\u001b[39m = \u001b[32m3\u001b[39m" ] }, "execution_count": 36, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Pair(1, 2).calc(_+_)\n", "// 길게 쓰면 아래와 같이 씀!" ] }, { "cell_type": "code", "execution_count": 39, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "\u001b[36mcaseInt\u001b[39m: \u001b[32mInt\u001b[39m = \u001b[32m3\u001b[39m" ] }, "execution_count": 39, "metadata": {}, "output_type": "execute_result" } ], "source": [ "val caseInt = Pair(1, 2).calc((a, b) => a+b)" ] }, { "cell_type": "code", "execution_count": 37, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "\u001b[36mres36\u001b[39m: \u001b[32mString\u001b[39m = \u001b[32m\"foobar\"\u001b[39m" ] }, "execution_count": 37, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Pair(\"foo\", \"bar\").calc(_+_)" ] }, { "cell_type": "code", "execution_count": 39, "metadata": {}, "outputs": [], "source": [ "// 짧아지긴 하지만.. 아직 적응이 잘 안될거에요.. 계속 하다보면..!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Methods to functions" ] }, { "cell_type": "code", "execution_count": 40, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "defined \u001b[32mobject\u001b[39m \u001b[36mAdder\u001b[39m" ] }, "execution_count": 40, "metadata": {}, "output_type": "execute_result" } ], "source": [ "object Adder {\n", " def add1(n: Int): Int = n+1\n", "}" ] }, { "cell_type": "code", "execution_count": 40, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "cmd40.sc:1: missing argument list for method add1 in object Adder\n", "Unapplied methods are only converted to functions when a function type is expected.\n", "You can make this conversion explicit by writing `add1 _` or `add1(_)` instead of `add1`.\n", "val res40 = Adder.add1\n", " ^" ] }, { "ename": "", "evalue": "", "output_type": "error", "traceback": [ "Compilation Failed" ] } ], "source": [ "Adder.add1\n", "// error 발생" ] }, { "cell_type": "code", "execution_count": 41, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "\u001b[36mres40\u001b[39m: \u001b[32mInt\u001b[39m => \u001b[32mInt\u001b[39m = " ] }, "execution_count": 41, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Adder.add1 _\n", "// 함수로 바꿔줌" ] }, { "cell_type": "code", "execution_count": 42, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "\u001b[36mres41\u001b[39m: \u001b[32mInt\u001b[39m => \u001b[32mInt\u001b[39m = " ] }, "execution_count": 42, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Adder.add1(_)\n", "// 함수로 바꿔줌" ] }, { "cell_type": "code", "execution_count": 43, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "defined \u001b[32mclass\u001b[39m \u001b[36mBox\u001b[39m" ] }, "execution_count": 43, "metadata": {}, "output_type": "execute_result" } ], "source": [ "case class Box[A](a: A) {\n", " def modify[B](f: A => B): B = f(a)\n", "}" ] }, { "cell_type": "code", "execution_count": 44, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "\u001b[36mres43\u001b[39m: \u001b[32mInt\u001b[39m = \u001b[32m43\u001b[39m" ] }, "execution_count": 44, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Box(42).modify(Adder.add1)" ] }, { "cell_type": "code", "execution_count": 45, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "\u001b[36mres44\u001b[39m: \u001b[32mInt\u001b[39m = \u001b[32m43\u001b[39m" ] }, "execution_count": 45, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Box(42) modify Adder.add1" ] }, { "cell_type": "code", "execution_count": 45, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "cmd45.sc:1: type mismatch;\n", " found : Int => Int\n", " required: String => ?\n", "val res45 = Box(\"foo\") modify Adder.add1\n", " ^" ] }, { "ename": "", "evalue": "", "output_type": "error", "traceback": [ "Compilation Failed" ] } ], "source": [ "Box(\"foo\") modify Adder.add1\n", "// add1은 Int를 받는 함수인데 foo는 string이라 error 발생" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## CURRING\n", "- 괄호가 여러개 쌓여있는 식으로 method를 만들 수 있음" ] }, { "cell_type": "code", "execution_count": 46, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "defined \u001b[32mfunction\u001b[39m \u001b[36mcurring\u001b[39m" ] }, "execution_count": 46, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def curring(x: Int)(y: Int): Int = x + y" ] }, { "cell_type": "code", "execution_count": 47, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "\u001b[36mres46\u001b[39m: \u001b[32mInt\u001b[39m = \u001b[32m3\u001b[39m" ] }, "execution_count": 47, "metadata": {}, "output_type": "execute_result" } ], "source": [ "curring(1)(2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 왜 이렇게 쓰느냐?\n", "- 하나에서 타입을 유추한 후, 그것을 다시 사용해야 하는 경우 커링을 써야 합니다!" ] }, { "cell_type": "code", "execution_count": 48, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "defined \u001b[32mfunction\u001b[39m \u001b[36mgeneric\u001b[39m" ] }, "execution_count": 48, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def generic[A, B](a: A, f: A => B): B = f(a)" ] }, { "cell_type": "code", "execution_count": 48, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "cmd48.sc:1: missing parameter type for expanded function ((x$1) => x$1.$plus(1))\n", "val res48 = generic(1, _ + 1)\n", " ^" ] }, { "ename": "", "evalue": "", "output_type": "error", "traceback": [ "Compilation Failed" ] } ], "source": [ "generic(1, _ + 1)\n", "// 왜 컴파일이 안될까요?\n", "// 파라미터 타입이 없음..!" ] }, { "cell_type": "code", "execution_count": 48, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "cmd48.sc:1: missing parameter type\n", "val res48 = generic(1, a=> a + 1)\n", " ^" ] }, { "ename": "", "evalue": "", "output_type": "error", "traceback": [ "Compilation Failed" ] } ], "source": [ "generic(1, a=> a + 1)\n", "// 타입 추론은 괄호 단위로만 가능. \n", "// a:A를 유추해 f:A를 쓸 수 없습니다. 명시적으로 적어줘야 함" ] }, { "cell_type": "code", "execution_count": 50, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "defined \u001b[32mfunction\u001b[39m \u001b[36mgenericCurring\u001b[39m" ] }, "execution_count": 50, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def genericCurring[A, B](a: A)(f: A => B): B = f(a)" ] }, { "cell_type": "code", "execution_count": 51, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "\u001b[36mres50\u001b[39m: \u001b[32mInt\u001b[39m = \u001b[32m2\u001b[39m" ] }, "execution_count": 51, "metadata": {}, "output_type": "execute_result" } ], "source": [ "genericCurring(1)(_ + 1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### EXERCISE: PAIRS\n", "- Pair 를 구현해보세요. 다음이 성립하도록 해야 합니다.\n", "\n" ] }, { "cell_type": "code", "execution_count": 54, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "ok\n" ] }, { "data": { "text/plain": [ "\u001b[36mpair1\u001b[39m: \u001b[32mPair\u001b[39m[\u001b[32mString\u001b[39m, \u001b[32mInt\u001b[39m] = \u001b[33mPair\u001b[39m(\u001b[32m\"foo\"\u001b[39m, \u001b[32m1\u001b[39m)\n", "\u001b[36mpair2\u001b[39m: \u001b[32mPair\u001b[39m[\u001b[32mInt\u001b[39m, \u001b[32mString\u001b[39m] = \u001b[33mPair\u001b[39m(\u001b[32m2\u001b[39m, \u001b[32m\"bar\"\u001b[39m)" ] }, "execution_count": 54, "metadata": {}, "output_type": "execute_result" } ], "source": [ "val pair1 = Pair(\"foo\", 1)\n", "val pair2 = Pair(2, \"bar\")\n", "assert(pair1.one == \"foo\")\n", "assert(pair1.two == 1)\n", "assert(pair2.one == 2)\n", "assert(pair2.two == \"bar\")\n", "\n", "println(\"ok\")" ] }, { "cell_type": "code", "execution_count": 52, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "defined \u001b[32mclass\u001b[39m \u001b[36mPair\u001b[39m" ] }, "execution_count": 52, "metadata": {}, "output_type": "execute_result" } ], "source": [ "case class Pair[A, B](one: A, two: B)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- 프로그래밍을 해보셨던 분들이 함수형 프로그래밍을 힘들어하는 경우가 많습니다,,,,,,,\n", "- 차라리 처음 하시는 분이 더 수월할 듯" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Tuple" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- Pair를 더 많은 원소를 가질 수 있도록 일반화한 Scala의 built-in 타입\n", "- Tuple1[A], Tuple2[A, B], ..., Tuple22[A, B, C, ...] \n", "- (A, B, C, ...)의 형태로도 사용가능 (syntactic sugar)" ] }, { "cell_type": "code", "execution_count": 55, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "\u001b[36mres54\u001b[39m: (\u001b[32mString\u001b[39m, \u001b[32mInt\u001b[39m) = (\u001b[32m\"foo\"\u001b[39m, \u001b[32m1\u001b[39m)" ] }, "execution_count": 55, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Tuple2(\"foo\", 1)" ] }, { "cell_type": "code", "execution_count": 56, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "\u001b[36mres55\u001b[39m: (\u001b[32mString\u001b[39m, \u001b[32mInt\u001b[39m) = (\u001b[32m\"foo\"\u001b[39m, \u001b[32m1\u001b[39m)" ] }, "execution_count": 56, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(\"foo\", 1)" ] }, { "cell_type": "code", "execution_count": 57, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "\u001b[36mres56\u001b[39m: (\u001b[32mString\u001b[39m, \u001b[32mInt\u001b[39m, \u001b[32mBoolean\u001b[39m) = (\u001b[32m\"foo\"\u001b[39m, \u001b[32m1\u001b[39m, \u001b[32mtrue\u001b[39m)" ] }, "execution_count": 57, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(\"foo\", 1, true)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 패턴 매칭" ] }, { "cell_type": "code", "execution_count": 58, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "\u001b[36mres57\u001b[39m: \u001b[32mString\u001b[39m = \u001b[32m\"foo1\"\u001b[39m" ] }, "execution_count": 58, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(\"foo\", 1) match { case (a, b) => a + b}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 원소 접근" ] }, { "cell_type": "code", "execution_count": 59, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "\u001b[36mx\u001b[39m: (\u001b[32mInt\u001b[39m, \u001b[32mString\u001b[39m, \u001b[32mBoolean\u001b[39m) = (\u001b[32m1\u001b[39m, \u001b[32m\"b\"\u001b[39m, \u001b[32mtrue\u001b[39m)" ] }, "execution_count": 59, "metadata": {}, "output_type": "execute_result" } ], "source": [ "val x = (1, \"b\", true)" ] }, { "cell_type": "code", "execution_count": 60, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "\u001b[36mres59\u001b[39m: \u001b[32mInt\u001b[39m = \u001b[32m1\u001b[39m" ] }, "execution_count": 60, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x._1" ] }, { "cell_type": "code", "execution_count": 61, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "\u001b[36mres60\u001b[39m: \u001b[32mBoolean\u001b[39m = \u001b[32mtrue\u001b[39m" ] }, "execution_count": 61, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x._3" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### EXERCISE: GENERIC SUM\n", "- 두 개의 서브타입 First와 Second를 가지는 Sum타입을 구현해보세요. 다음 테스트가 통과되도록 하셔야 합니다.\n", "\n" ] }, { "cell_type": "code", "execution_count": 64, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "defined \u001b[32mtrait\u001b[39m \u001b[36mSum\u001b[39m\n", "defined \u001b[32mclass\u001b[39m \u001b[36mFirst\u001b[39m\n", "defined \u001b[32mclass\u001b[39m \u001b[36mSecond\u001b[39m" ] }, "execution_count": 64, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sealed trait Sum[A, B]\n", "final case class First[A, B]() extends Sum[A, B]\n", "final case class Second[A, B]() extends Sum[A, B]" ] }, { "cell_type": "code", "execution_count": 62, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "cmd62.sc:1: too many arguments for method apply: ()cmd62Wrapper.this.cmd61.wrapper.First[Int,String] in object First\n", "val res62_0 = assert(First[Int, String](1).value == 1)\n", " ^cmd62.sc:2: too many arguments for method apply: ()cmd62Wrapper.this.cmd61.wrapper.Second[Int,String] in object Second\n", "val res62_1 = assert(Second[Int, String](\"foo\").value == \"foo\")\n", " ^cmd62.sc:3: too many arguments for method apply: ()cmd62Wrapper.this.cmd61.wrapper.Second[A,B] in object Second\n", "val sum: Sum[Int, String] = Second(\"foo\")\n", " ^cmd62.sc:5: wrong number of arguments for pattern cmd62Wrapper.this.cmd61.wrapper.First[Int,String]()\n", " case First(x) => x.toString\n", " ^cmd62.sc:6: wrong number of arguments for pattern cmd62Wrapper.this.cmd61.wrapper.Second[Int,String]()\n", " case Second(x) => x\n", " ^" ] }, { "ename": "", "evalue": "", "output_type": "error", "traceback": [ "Compilation Failed" ] } ], "source": [ "assert(First[Int, String](1).value == 1)\n", "assert(Second[Int, String](\"foo\").value == \"foo\")\n", "val sum: Sum[Int, String] = Second(\"foo\")\n", "val matched: String = sum match {\n", " case First(x) => x.toString\n", " case Second(x) => x\n", "}\n", "assert(matched == \"foo\")\n", "println(\"ok\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 답" ] }, { "cell_type": "code", "execution_count": 68, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "defined \u001b[32mtrait\u001b[39m \u001b[36mSum\u001b[39m\n", "defined \u001b[32mclass\u001b[39m \u001b[36mFirst\u001b[39m\n", "defined \u001b[32mclass\u001b[39m \u001b[36mSecond\u001b[39m" ] }, "execution_count": 68, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sealed trait Sum[A, B]\n", "final case class First[A, B](value: A) extends Sum[A, B]\n", "final case class Second[A, B](value: B) extends Sum[A, B]" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "// 인자를 1개만 받음\n", "// .value가 써있기 때문에 value를 넣어주면 됨" ] }, { "cell_type": "code", "execution_count": 69, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "ok\n" ] }, { "data": { "text/plain": [ "\u001b[36msum\u001b[39m: \u001b[32mSum\u001b[39m[\u001b[32mInt\u001b[39m, \u001b[32mString\u001b[39m] = Second(foo)\n", "\u001b[36mmatched\u001b[39m: \u001b[32mString\u001b[39m = \u001b[32m\"foo\"\u001b[39m" ] }, "execution_count": 69, "metadata": {}, "output_type": "execute_result" } ], "source": [ "assert(First[Int, String](1).value == 1)\n", "assert(Second[Int, String](\"foo\").value == \"foo\")\n", "val sum: Sum[Int, String] = Second(\"foo\") \n", "val matched: String = sum match {\n", " case First(x) => x.toString\n", " case Second(x) => x\n", "}\n", "assert(matched == \"foo\")\n", "println(\"ok\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### EXERCISE: MAYBE\n", "- 두 개의 서브타입 Just와 Empty를 가지는 Maybe타입을 구현해보세요. 다음 테스트가 통과되도록 하셔야 합니다.\n" ] }, { "cell_type": "code", "execution_count": 71, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "cmd71.sc:6: too many arguments for method apply: ()cmd71Wrapper.this.cmd70.wrapper.Just[A] in object Just\n", "val res71_1 = assert(divide(10, 2) == Just(5))\n", " ^cmd71.sc:3: too many arguments for method apply: ()cmd71Wrapper.this.cmd70.wrapper.Just[A] in object Just\n", " case _ => Just(a/b)\n", " ^" ] }, { "ename": "", "evalue": "", "output_type": "error", "traceback": [ "Compilation Failed" ] } ], "source": [ "def divide(a: Int, b: Int): Maybe[Int] = b match {\n", " case 0 => Empty()\n", " case _ => Just(a/b)\n", "}\n", "\n", "assert(divide(10, 2) == Just(5))\n", "assert(divide(10, 0) == Empty())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 답" ] }, { "cell_type": "code", "execution_count": 73, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "defined \u001b[32mtrait\u001b[39m \u001b[36mMaybe\u001b[39m\n", "defined \u001b[32mclass\u001b[39m \u001b[36mEmpty\u001b[39m\n", "defined \u001b[32mclass\u001b[39m \u001b[36mJust\u001b[39m" ] }, "execution_count": 73, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sealed trait Maybe[A]\n", "final case class Empty[A]() extends Maybe[A]\n", "final case class Just[A](value: A) extends Maybe[A]" ] }, { "cell_type": "code", "execution_count": 74, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "defined \u001b[32mfunction\u001b[39m \u001b[36mdivide\u001b[39m" ] }, "execution_count": 74, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def divide(a: Int, b: Int): Maybe[Int] = b match {\n", " case 0 => Empty()\n", " case _ => Just(a/b)\n", "}\n", "\n", "assert(divide(10, 2) == Just(5))\n", "assert(divide(10, 0) == Empty())" ] }, { "cell_type": "code", "execution_count": 76, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "\u001b[36mres75\u001b[39m: \u001b[32mMaybe\u001b[39m[\u001b[32mInt\u001b[39m] = Just(10)" ] }, "execution_count": 76, "metadata": {}, "output_type": "execute_result" } ], "source": [ "divide(10,1)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### EXERCISE: MAP\n", "- 앞서 작성했던 LinkedList에 map메소드를 구현해보세요.\n", "\n", "- 타입 시그니쳐는 다음과 같습니다." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sealed trait LinkedList[A]{\n", " def map[B](f: A => B): LinkedList[B] = \n", "}\n", "final case class End[A]() extends LinkedList[A]\n", "final case class Pair[A](head: A, tail: LinkedList[A]) extends LinkedList[A]" ] }, { "cell_type": "code", "execution_count": 76, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "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]]]]\n", "val res76_1 = assert(l.map(_ + 1) == Pair(2, Pair(3, Pair(4, End()))))\n", " ^" ] }, { "ename": "", "evalue": "", "output_type": "error", "traceback": [ "Compilation Failed" ] } ], "source": [ "val l = Pair(1, Pair(2, Pair(3, End())))\n", "assert(l.map(_ + 1) == Pair(2, Pair(3, Pair(4, End()))))\n", "\n", "println(\"ok\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 답" ] }, { "cell_type": "code", "execution_count": 77, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "defined \u001b[32mtrait\u001b[39m \u001b[36mLinkedList\u001b[39m\n", "defined \u001b[32mclass\u001b[39m \u001b[36mEnd\u001b[39m\n", "defined \u001b[32mclass\u001b[39m \u001b[36mPair\u001b[39m" ] }, "execution_count": 77, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sealed trait LinkedList[A]{\n", " def map[B](f: A => B): LinkedList[B] = this match {\n", " case End() => End[B]()\n", " case Pair(head, tail) => Pair[B](f(head) , tail.map(f))\n", " } \n", "}\n", "final case class End[A]() extends LinkedList[A]\n", "final case class Pair[A](head: A, tail: LinkedList[A]) extends LinkedList[A]" ] }, { "cell_type": "code", "execution_count": 78, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "ok\n" ] }, { "data": { "text/plain": [ "\u001b[36ml\u001b[39m: \u001b[32mPair\u001b[39m[\u001b[32mInt\u001b[39m] = Pair(1,Pair(2,Pair(3,End())))" ] }, "execution_count": 78, "metadata": {}, "output_type": "execute_result" } ], "source": [ "val l = Pair(1, Pair(2, Pair(3, End())))\n", "assert(l.map(_ + 1) == Pair(2, Pair(3, Pair(4, End()))))\n", "\n", "println(\"ok\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### EXERCISE: FOLD\n", "- LinkedList에 fold메소드를 구현해보세요.\n", "\n", "- 타입 시그니쳐는 다음과 같습니다" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def fold[B](init: B)(f: (A, B) => B): B = ???" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "// test\n", "val list0 = Pair(1, Pair(2, Pair(3, Pair(4, End))))\n", "assert(list0.fold(0)(_ + _) == 10)\n", "val list1 = Pair(1, Pair(2, Pair(3, Pair(4, End))))\n", "assert(list1.fold(1)(_ * _) == 24)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 답" ] }, { "cell_type": "code", "execution_count": 79, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "defined \u001b[32mtrait\u001b[39m \u001b[36mLinkedList\u001b[39m\n", "defined \u001b[32mclass\u001b[39m \u001b[36mEnd\u001b[39m\n", "defined \u001b[32mclass\u001b[39m \u001b[36mPair\u001b[39m" ] }, "execution_count": 79, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sealed trait LinkedList[A]{\n", " def fold[B](init: B)(f: (A, B) => B): B = this match{\n", " case End() => init\n", " case Pair(head, tail) => f(head, tail.fold(init)(f))\n", " }\n", "}\n", "final case class End[A]() extends LinkedList[A]\n", "final case class Pair[A](head: A, tail: LinkedList[A]) extends LinkedList[A]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### EXERCISE: FLATMAP\n", "- LinkedList에 flatMap메소드를 구현해보세요.\n", "- 하나를 여러개로 만드는 것\n", "- 타입 시그니쳐" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "// test\n", "val list = Pair(1, Pair(2, Pair(3, Pair(4, End))))\n", "val filtered = list.flatMap{\n", " case n if x % 2 == 0 => End\n", " case n => Pair(n, End)\n", "}\n", "assert(filtered == Pair(1, Pair(3, End)))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 답" ] }, { "cell_type": "code", "execution_count": 80, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Ok!\n" ] }, { "data": { "text/plain": [ "defined \u001b[32mtrait\u001b[39m \u001b[36mLinkedList\u001b[39m\n", "defined \u001b[32mclass\u001b[39m \u001b[36mEnd\u001b[39m\n", "defined \u001b[32mclass\u001b[39m \u001b[36mPair\u001b[39m\n", "\u001b[36mlist\u001b[39m: \u001b[32mwrapper\u001b[39m.\u001b[32mwrapper\u001b[39m.\u001b[32mPair\u001b[39m[\u001b[32mInt\u001b[39m] = Pair(1,Pair(2,Pair(3,Pair(4,End()))))\n", "\u001b[36mfiltered\u001b[39m: \u001b[32mwrapper\u001b[39m.\u001b[32mwrapper\u001b[39m.\u001b[32mLinkedList\u001b[39m[\u001b[32mInt\u001b[39m] = Pair(1,Pair(3,End()))" ] }, "execution_count": 80, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sealed trait LinkedList[A] {\n", " def map[B](f: A => B): LinkedList[B] = this match {\n", " case End() => End[B]()\n", " case Pair(head, tail) => Pair[B](f(head), tail.map(f))\n", " }\n", " def fold[B](init: B)(f: (A, B) => B): B = this match {\n", " case End() => init\n", " case Pair(head, tail) => f(head, tail.fold(init)(f))\n", " }\n", " def flatMap[B](f: A => LinkedList[B]): LinkedList[B] = this match {\n", " case End() => End()\n", " case Pair(head, tail) =>\n", " val headList: LinkedList[B] = f(head)\n", " val tailList: LinkedList[B] = tail.flatMap(f)\n", " headList ++ tailList\n", " }\n", " def ++(that: LinkedList[A]): LinkedList[A] = this match {\n", " case End() => that\n", " case Pair(head, tail) => Pair(head, tail ++ that)\n", " }\n", "}\n", "final case class End[A]() extends LinkedList[A]\n", "final case class Pair[A](head: A, tail: LinkedList[A]) extends LinkedList[A]\n", "val list = Pair(1, Pair(2, Pair(3, Pair(4, End()))))\n", "val filtered = list.flatMap[Int]{\n", " case n if n % 2 == 0 => End()\n", " case n => Pair(n, End())\n", "}\n", "assert(filtered == Pair(1, Pair(3, End())))\n", "\n", "println(\"Ok!\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## VARIANCE (주의: 어려움)\n", "- variance에 대해 얼핏 알고.. 디버깅하는 노하우를 알려드리겠습니다" ] }, { "cell_type": "code", "execution_count": 81, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "defined \u001b[32mtrait\u001b[39m \u001b[36mMaybe\u001b[39m\n", "defined \u001b[32mclass\u001b[39m \u001b[36mJust\u001b[39m\n", "defined \u001b[32mclass\u001b[39m \u001b[36mEmpty\u001b[39m" ] }, "execution_count": 81, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sealed trait Maybe[A]\n", "final case class Just[A](value: A) extends Maybe[A]\n", "final case class Empty[A]() extends Maybe[A]" ] }, { "cell_type": "code", "execution_count": 81, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "cmd81.sc:3: not found: type ???\n", "final case object Empty extends Maybe[???]\n", " ^" ] }, { "ename": "", "evalue": "", "output_type": "error", "traceback": [ "Compilation Failed" ] } ], "source": [ "// 인자가 없으니 아래처럼 만들면 간단한데!\n", "sealed trait Maybe[A]\n", "final case class Just[A](value: A) extends Maybe[A]\n", "final case object Empty extends Maybe[???]" ] }, { "cell_type": "code", "execution_count": 82, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "defined \u001b[32mtrait\u001b[39m \u001b[36mMaybe\u001b[39m\n", "defined \u001b[32mclass\u001b[39m \u001b[36mJust\u001b[39m\n", "defined \u001b[32mobject\u001b[39m \u001b[36mEmpty\u001b[39m" ] }, "execution_count": 82, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sealed trait Maybe[A]\n", "final case class Just[A](value: A) extends Maybe[A]\n", "final case object Empty extends Maybe[Nothing]\n", "// Nothing : 어떤 것들의 sub type!!!" ] }, { "cell_type": "code", "execution_count": 82, "metadata": {}, "outputs": [], "source": [ "// 명시적으로 해주는 것을 variance" ] }, { "cell_type": "code", "execution_count": 82, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "cmd82.sc:1: type mismatch;\n", " found : cmd82Wrapper.this.cmd81.wrapper.Empty.type\n", " required: cmd82Wrapper.this.cmd81.wrapper.Maybe[Int]\n", "Note: Nothing <: Int (and cmd82Wrapper.this.cmd81.wrapper.Empty.type <: cmd82Wrapper.this.cmd81.wrapper.Maybe[Nothing]), but trait Maybe is invariant in type A.\n", "You may wish to define A as +A instead. (SLS 4.5)\n", "val perhaps: Maybe[Int] = Empty\n", " ^" ] }, { "ename": "", "evalue": "", "output_type": "error", "traceback": [ "Compilation Failed" ] } ], "source": [ "val perhaps: Maybe[Int] = Empty" ] }, { "cell_type": "code", "execution_count": 83, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "defined \u001b[32mtrait\u001b[39m \u001b[36mMaybe\u001b[39m\n", "defined \u001b[32mclass\u001b[39m \u001b[36mJust\u001b[39m\n", "defined \u001b[32mobject\u001b[39m \u001b[36mEmpty\u001b[39m" ] }, "execution_count": 83, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sealed trait Maybe[+A]\n", "final case class Just[A](value: A) extends Maybe[A]\n", "final case object Empty extends Maybe[Nothing]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "![image](https://www.dropbox.com/s/ozha2x75wtkm342/%EC%8A%A4%ED%81%AC%EB%A6%B0%EC%83%B7%202018-05-08%2022.04.19.png?raw=1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- 왠만하면 covariant일 것!\n", "- 함수의 인자로 타입을 쓰는 경우엔 contravariant도 있음" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "trait Function0[+R] {\n", " def apply: R\n", "}\n", "\n", "trait Function1[-A, +B] {\n", " def apply(a: A): B\n", "}\n", "\n", "trait Function2[-A, -B, +C] {\n", " def apply(a: A, b: B): C\n", "}" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "case class Box[A](a: A) {\n", " def map[B](f: Function1[A, B]): Box[B] = Box(f(a))\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### CONTRAVARIANT POSITION\n" ] }, { "cell_type": "code", "execution_count": 83, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "cmd83.sc:2: covariant type A occurs in contravariant position in type A of value a\n", " def set(a: A): Box[A] = Box(a) // 컴파일 안됨!\n", " ^" ] }, { "ename": "", "evalue": "", "output_type": "error", "traceback": [ "Compilation Failed" ] } ], "source": [ "case class Box[+A](a: A) {\n", " def set(a: A): Box[A] = Box(a) // 컴파일 안됨! 인자에만 들어갈 수 있음\n", "}" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "case class Box[+A](a: A) {\n", " def set[AA >: A](a: AA): Box[AA] = Box(a) \n", "}\n", "// AA라는 super type이 있을 경우 AA를 넣어서 Box type을 만든다^^.. 이해 안가죠?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Type Bound" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- A <: B\t: A is a subtype of B\n", "- A >: B\t: A is a supertype of B" ] }, { "cell_type": "code", "execution_count": 83, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "cmd83.sc:2: covariant type A occurs in contravariant position in type B => Helper.this.Sum[A,C] of value f\n", " def flatMap[C](f: B => Sum[A, C]): Sum[A, C] = this match {\n", " ^" ] }, { "ename": "", "evalue": "", "output_type": "error", "traceback": [ "Compilation Failed" ] } ], "source": [ "sealed trait Sum[+A, +B] {\n", " def flatMap[C](f: B => Sum[A, C]): Sum[A, C] = this match {\n", " case Failure(v) => Failure(v)\n", " case Success(v) => f(v)\n", " }\n", "}\n", "final case class Failure[A](value: A) extends Sum[A, Nothing]\n", "final case class Success[B](value: B) extends Sum[Nothing, B]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- 아 variance 잘못 썼구나! 아래처럼 수정.." ] }, { "cell_type": "code", "execution_count": 84, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "defined \u001b[32mtrait\u001b[39m \u001b[36mSum\u001b[39m\n", "defined \u001b[32mclass\u001b[39m \u001b[36mFailure\u001b[39m\n", "defined \u001b[32mclass\u001b[39m \u001b[36mSuccess\u001b[39m" ] }, "execution_count": 84, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sealed trait Sum[+A, +B] {\n", " def flatMap[AA >: A, C](f: B => Sum[AA, C]): Sum[AA, C] = this match {\n", " case Failure(v) => Failure(v)\n", " case Success(v) => f(v)\n", " }\n", "}\n", "final case class Failure[A](value: A) extends Sum[A, Nothing]\n", "final case class Success[B](value: B) extends Sum[Nothing, B]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### EXERCISE: CALCULATOR\n", "- 다음에서 eval 메소드를 구현해보세요\n", "- Generic sub type\n", "- Tuple 활용\n", "- 숙제" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sealed trait Expression {\n", " def eval: Sum[String, Double] = ???\n", "}\n", "final case class Addition(left: Expression, right: Expression) extends Expression\n", "final case class Subtraction(left: Expression, right: Expression) extends Expression\n", "final case class Division(left: Expression, right: Expression) extends Expression\n", "final case class SquareRoot(value: Expression) extends Expression\n", "final case class Number(value: Double) extends Expression" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "// test\n", "assert(Addition(Number(1), Number(2)).eval == Success(3))\n", "assert(SquareRoot(Number(-1)).eval == Failure(\"Square root of negative number\"))\n", "assert(Division(Number(4), Number(0)).eval == Failure(\"Division by zero\"))\n", "assert(Division(Addition(Subtraction(Number(8), Number(6)), Number(2)), Number(2)).eval == Success(2.0))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Scala", "language": "scala", "name": "scala" }, "language_info": { "codemirror_mode": "text/x-scala", "file_extension": ".scala", "mimetype": "text/x-scala", "name": "scala211", "nbconvert_exporter": "script", "pygments_lexer": "scala", "version": "2.11.11" }, "varInspector": { "cols": { "lenName": 16, "lenType": 16, "lenVar": 40 }, "kernels_config": { "python": { "delete_cmd_postfix": "", "delete_cmd_prefix": "del ", "library": "var_list.py", "varRefreshCmd": "print(var_dic_list())" }, "r": { "delete_cmd_postfix": ") ", "delete_cmd_prefix": "rm(", "library": "var_list.r", "varRefreshCmd": "cat(var_dic_list()) " } }, "types_to_exclude": [ "module", "function", "builtin_function_or_method", "instance", "_Feature" ], "window_display": false } }, "nbformat": 4, "nbformat_minor": 2 }