paradigm FP OOP,
FP
FPFP
typing static static dynamic dynamic
first
appeared
1990 2004 2007 2012
related ML ML Lisp Lisp
🐬's note lazy/pure
FPL
standard
OOPL
in
FPL's
skin
modern
functional
Lisp
Erlang +
Ruby +
Clojure
Haskell Scala Clojure Elixir
30
31.
モジュール定義とトップレベル変数
{- Haskell -}
--モジュールとそのエクスポートリスト
module SomeModule(a) where
-- パブリック変数
a :: Int
a = 1
-- プライベート変数
b :: Int
b = 2
/* Scala */
// (シングルトン)オブジェクト
object SomeModule:
// パブリック変数
val a: Int = 1
// プライベート変数
private val b: Int = 2
31
32.
;;; Clojure
;; 名前空間
(nssome-module)
;; パブリック変数
(def a 1)
;; プライベート変数
(def ^:private b 2)
## Elixir
# モジュール
defmodule SomeModule do
# パブリックな0引数関数
def a, do: 1
# プライベートな0引数関数
defp b, do: 2
end
32
33.
トップレベル関数
{- Haskell -}
moduleSimpleMath(square) where
-- パブリック関数
square :: Int -> Int
square n = n * n
-- プライベート関数
double :: Int -> Int
double n = n * 2
/* Scala */
object SimpleMath:
// パブリックメソッド
def square(n: Int): Int =
n * n
// プライベートメソッド
private def double(n: Int): Int =
n * 2
33
34.
;;; Clojure
(ns simple-math)
;;パブリック関数
(defn square [n] (* n n))
;; プライベート関数
(defn- double' [n] (* n 2))
## Elixir
defmodule SimpleMath do
# パブリック関数
def square(n), do: n * n
# プライベート関数
defp double(n), do: n * 2
end
34
35.
モジュールの利用
{- Haskell: ghciコマンドによるREPL-}
λ> :l SimpleMath
...
λ> import qualified SimpleMath as M -- モジュールを別名で参照
λ> M.square 3
9
it :: Int
λ> map M.square [0..10]
[0,1,4,9,16,25,36,49,64,81,100]
it :: [Int]
/* Scala: scalaコマンドによるREPL */
scala> :l SimpleMath.scala
// defined object SimpleMath
scala> import SimpleMath as M // オブジェクトを別名で参照
scala> M.square(3)
val res0: Int = 9
scala> (0 to 10).map(M.square)
val res1: IndexedSeq[Int] = Vector(0, 1, 4, 9, 16, 25, 36,
49, 64, 81, 100)
35
ローカル束縛(変数)
{- Haskell -}
λ>:{ -- REPLでの複数行入力の開始
λ| -- ML系言語でお馴染み(?)のlet式
λ| let x = 5
λ| y = M.square(x)
λ| in "square " ++ show x ++ " = " ++ show y
λ| :} -- REPLでの複数行入力の終了
"square 5 = 25"
it :: [Char]
/* Scala */
scala> val x = 5
val x: Int = 5
scala> val y = M.square(x)
val y: Int = 25
scala> s"square($x) = $y"
val res2: String = square(5) = 25
37
無名関数(ラムダ式)
{- Haskell -}
λ>(n -> n * n * n) 2
8
it :: Num a => a
λ> map (n -> n * n * n) [0..10]
[0,1,8,27,64,125,216,343,512,729,1000]
it :: (Num b, Enum b) => [b]
/* Scala */
scala> ((n: Int) => n * n * n)(2)
val res0: Int = 8
scala> (0 to 10).map(n => n * n * n)
val res1: IndexedSeq[Int] = Vector(0, 1, 8, 27, 64, 125, 216,
343, 512, 729, 1000)
39
40.
;;; Clojure
user=> ((fn[n] (* n n n )) 2)
8
user=> (#(* % % %) 2) ; 無名関数の略記法
8
user=> (map (fn [n] (* n n n )) (range 0 (inc 10)))
(0 1 8 27 64 125 216 343 512 729 1000)
user=> (map #(* % % %) (range 0 (inc 10)))
(0 1 8 27 64 125 216 343 512 729 1000)
## Elixir
iex(1)> (fn n -> n * n * n end).(2)
8
iex(2)> (&(&1 * &1 * &1)).(2) # 無名関数の略記法
8
iex(3)> Enum.map(0..10, fn n -> n * n * n end)
[0, 1, 8, 27, 64, 125, 216, 343, 512, 729, 1000]
iex(4)> Enum.map(0..10, &(&1 * &1 * &1))
[0, 1, 8, 27, 64, 125, 216, 343, 512, 729, 1000]
40
41.
再帰と無限リスト(1): 階乗
{- Haskell-}
module Factorial where
factorial :: Integral a => a -> a
factorial 0 = 1
factorial n = n * factorial (n - 1)
factorialSeq :: Integral a => [a]
factorialSeq = scanl (*) 1 [1..]
-- import Factorial (factorialSeq)
λ> take 10 factorialSeq
[1,1,2,6,24,120,720,5040,40320,362880]
it :: Integral a => [a]
λ> factorialSeq !! 100
9332621544394415268169923885626670049071596826438162146859296
3895217599993229915608941463976156518286253697920827223758251
185210916864000000000000000000000000
it :: Integral a => a
41
42.
/* Scala */
objectFactorial:
def factorial(n: Int): BigInt = n match
case 0 => 1
case _ => n * factorial(n - 1)
def factorialSeq: LazyList[BigInt] =
LazyList.from(1).scanLeft(BigInt(1))(_ * _)
// import Factorial.factorialSeq
scala> factorialSeq.take(10).toList
val res0: List[BigInt] = List(1, 1, 2, 6, 24, 120, 720, 5040,
40320, 362880)
scala> factorialSeq(100)
val res1: BigInt = 933262154439441526816992388562667004907159
6826438162146859296389521759999322991560894146397615651828625
3697920827223758251185210916864000000000000000000000000
42
再帰と無限リスト(2): フィボナッチ数
{- Haskell-}
module Fibonacci where
fib :: Integral a => a -> a
fib 0 = 0
fib 1 = 1
fib n = fib (n - 1) + fib (n - 2)
fibSeq :: Integral a => [a]
fibSeq = map fst $ iterate ((a, b) -> (b, a + b)) (0, 1)
-- import Fibonacci (fibSeq)
λ> take 10 fibSeq
[0,1,1,2,3,5,8,13,21,34]
it :: Integral a => [a]
λ> take 3 $ drop 100 fibSeq
[354224848179261915075,573147844013817084101,
927372692193078999176]
it :: Integral a => [a]
45
46.
/* Scala */
objectFibonacci:
def fib(n: Int): BigInt = n match
case 0 => 0
case 1 => 1
case _ => fib(n - 1) + fib(n - 2)
def fibSeq: LazyList[BigInt] =
LazyList.iterate((BigInt(0), BigInt(1))) {
case (a, b) => (b, a + b)
}.map(_(0))
// import Fibonacci.fibSeq
scala> fibSeq.take(10).toList
val res2: List[BigInt] = List(0, 1, 1, 2, 3, 5, 8, 13, 21,
34)
scala> fibSeq.drop(100).take(3).toList
val res3: List[BigInt] = List(354224848179261915075,
573147844013817084101, 927372692193078999176)
46
部分適用
{- Haskell -}
λ>:t map -- 関数はカリー(1引数関数の連鎖)化されている
map :: (a -> b) -> [a] -> [b]
λ> :t (+) -- 演算子もカリー化されている
(+) :: Num a => a -> a -> a
λ> :t (+ 1) -- x -> x + 1 と等価(セクション記法による部分適用)
(+ 1) :: Num a => a -> a
λ> :t map (+ 1) -- xs -> map (+ 1) xs と等価(部分適用)
map (+ 1) :: Num b => [b] -> [b]
λ> map (+ 1) [0..9]
[1,2,3,4,5,6,7,8,9,10]
it :: (Num b, Enum b) => [b]
λ> f x y z = x * y * z
f :: Num a => a -> a -> a -> a
λ> :t f 2 3 -- 2番目の引数まで部分適用
f 2 3 :: Num a => a -> a
λ> f 2 3 4
24
it :: Num a => a
54
55.
/* Scala */
scala>:t (1 to 10).map // メソッドが関数になる(eta-expansion)
(Int => Any) => IndexedSeq[Any]
scala> :t (_: Int) + (_: Int) // (x, y) => x + y と等価
(Int, Int) => Int
scala> :t (_: Int) + 1 // x => x + 1 と等価(部分適用)
Int => Int
scala> (0 to 9).map(_ + 1)
val res0: IndexedSeq[Int] = Vector(1, 2, 3, 4, 5, 6, 7, 8, 9,
10)
scala> def f(x: Int)(y: Int)(z: Int): Int = x * y * z
def f(x: Int)(y: Int)(z: Int): Int
scala> f(2)(3) // 2番目の引数まで部分適用
val res1: Int => Int = Lambda/0x00001ff001554c40@478c84aa
scala> f(2)(3)(4)
val res2: Int = 24
55
56.
;;; Clojure
user=> #(+%1 %2 %3) ; (fn [x y z] (+ x y z)) と等価
#object[user$eval245$fn__246 0x7103ab0 "user$eval245$fn__246@
7103ab0"]
user=> #(+ % 1) ; (fn [x] (+ x 1)) と等価(部分適用)
#object[user$eval235$fn__236 0x4c03a37 "user$eval235$fn__236@
4c03a37"]
user=> (map #(+ % 1) (range 0 (inc 9)))
(1 2 3 4 5 6 7 8 9 10)
user=> (defn f [x y z] (* x y z))
#'user/f
user=> (partial f 2 3) ; 2番目の引数まで部分適用 = #(f 2 3 %)
#object[clojure.core$partial$fn__5929 0x4ba89729 "clojure.cor
e$partial$fn__5929@4ba89729"]
user=> ((partial f 2 3) 4)
24
user=> (f 2 3 4)
24
56
57.
## Elixir
iex(1)> &(&1+ &2) # fn (x, y) -> x + y end と等価
&:erlang.+/2
iex(2)> &(&1 + 1) # fn x -> x + 1 end と等価(部分適用)
#Function<42.81571850/1 in :erl_eval.expr/6>
iex(3)> Enum.map(0..9, &(&1 + 1))
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
iex(4)> f = fn x -> fn y -> fn z -> x * y * z end end end
#Function<42.81571850/1 in :erl_eval.expr/6>
iex(5)> f.(2).(3) # 2番目の引数まで部分適用
#Function<42.81571850/1 in :erl_eval.expr/6>
iex(6)> f.(2).(3).(4)
24
57
58.
関数合成
ref.
{- Haskell: importFibonacci (fibSeq) -}
λ> sumOfEvenFibs upper = sum (takeWhile (<= upper) (filter
even (drop 2 fibSeq)))
sumOfEvenFibs :: Integral a => a -> a
λ> :{
λ| sumOfEvenFibs' upper = sum
λ| $ takeWhile (<= upper) -- f $ x = f x
λ| $ filter even -- (関数適用演算子)
λ| $ drop 2 fibSeq
λ| :}
sumOfEvenFibs' :: Integral a => a -> a
λ> sumOfEvenFibs' 4000000
4613732
it :: Integral a => a
#2 Even Fibonacci Numbers - Project Euler
58
59.
{- Haskell -}
λ>:{
λ| sumOfEvenNums :: Integral a => a -> [a] -> a
λ| sumOfEvenNums upper = sum
λ| . takeWhile (<= upper) -- f . g = x -> f (g x)
λ| . filter even -- (関数合成演算子)
λ| :}
sumOfEvenNums :: Integral a => a -> [a] -> a
λ> sumOfEvenNums 4000000 $ drop 2 fibSeq
4613732
it :: Integral a => a
59
標準の不変/永続コレクション
{- Haskell -}
λ>:t [1, 2, 3] -- (連結)リスト
[1, 2, 3] :: Num a => [a]
λ> 0 : [1, 2, 3] -- 先頭に要素追加(cons)
[0,1,2,3]
it :: Num a => [a]
λ> import qualified Data.Vector as V
λ> :t V.fromList [1, 2, 3] -- ベクター(可変長配列)
V.fromList [1, 2, 3] :: Num a => V.Vector a
λ> V.snoc (V.fromList [1, 2, 3]) 4 -- 末尾に要素追加(snoc)
[1,2,3,4]
it :: Num a => V.Vector a
71
72.
{- Haskell -}
λ>import qualified Data.Set as S
λ> :t S.fromList [1, 2, 3] -- セット
S.fromList [1, 2, 3] :: (Ord a, Num a) => S.Set a
λ> S.insert 4 $ S.fromList [1, 2, 3]
fromList [1,2,3,4]
it :: (Ord a, Num a) => S.Set a
λ> import qualified Data.Map as M
λ> :t M.fromList [("a", 1), ("b", 2), ("c", 3)] -- マップ
M.fromList [("a", 1), ("b", 2), ("c", 3)]
:: Num a => M.Map String a
λ> M.insert "d" 4 $ M.fromList [("a", 1), ("b", 2), ("c", 3)]
fromList [("a",1),("b",2),("c",3),("d",4)]
it :: Num a => M.Map String a
72
73.
{- Haskell -}
λ>[x * y | x <- [1..2], y <- [1..9]] -- リスト内包表記
[1,2,3,4,5,6,7,8,9,2,4,6,8,10,12,14,16,18]
it :: (Enum a, Num a) => [a]
λ> :{
λ| do -- do記法(a.k.a. モナド内包表記)
λ| x <- [1..2]
λ| y <- [1..9]
λ| return $ x * y
λ| :}
[1,2,3,4,5,6,7,8,9,2,4,6,8,10,12,14,16,18]
it :: (Num b, Enum b) => [b]
λ> :{
λ| do -- Maybe, Eitherなど任意のモナドで利用できる
λ| x <- Just 2
λ| y <- Just 3
λ| return $ x * y
λ| :}
Just 6
it :: Num b => Maybe b
73
データ型の定義と値の構築・分解/分岐
{- Haskell -}
λ>:{
λ| data Tree a -- 代数的データ型の定義
λ| = Leaf !a
λ| | Branch { left :: !(Tree a)
λ| , right :: !(Tree a)
λ| }
λ| deriving (Show, Eq)
λ| :}
type Tree :: * -> *
data Tree a = ...
left :: Tree a -> Tree a
right :: Tree a -> Tree a
83
84.
{- Haskell -}
λ>t = Branch (Branch (Leaf 1) (Branch (Leaf 2) (Leaf 3)))
(Leaf 4)
t :: Num a => Tree a
[Prelude]
λ> left t
Branch {left = Leaf 1, right = Branch {left = Leaf 2, right =
Leaf 3}}
it :: Num a => Tree a
λ> right t
Leaf 4
it :: Num a => Tree a
λ> :{
λ| size :: Tree a -> Int
λ| size (Leaf _) = 1 -- 関数の引数でのパターンマッチ
λ| size (Branch l r) = 1 + size l + size r
λ| :}
size :: Tree a -> Int
λ> size t
7
it :: Int
84
85.
/* Scala */
scala>enum Tree[+A]: // 代数的データ型の定義
| case Leaf(value: A)
| case Branch(left: Tree[A], right: Tree[A])
|
// defined class Tree
scala> import Tree.*
scala> val t: Branch[Int] = Branch(Branch(Leaf(1), Branch(
Leaf(2), Leaf(3))), Leaf(4))
val t: Tree.Branch[Int] = Branch(Branch(Leaf(1),Branch(
Leaf(2),Leaf(3))),Leaf(4))
scala> t.left
val res0: Tree[Int] = Branch(Leaf(1),Branch(Leaf(2),Leaf(3)))
scala> t.right
val res1: Tree[Int] = Leaf(4)
85
86.
/* Scala */
scala>extension [A](tree: Tree[A]) // 拡張メソッドの定義
| def size: Int = tree match
| case Leaf(_) => 1
| case Branch(l, r) => 1 + l.size + r.size
|
def size[A](tree: Tree[A]): Int
scala> t.size
val res2: Int = 7
86
評価の制御と遅延コレクション
{- Haskell: 言語のデフォルトの評価戦略は遅延(非正格)評価-}
-- 関数の定義: call by need
λ> f x y = if x > 0 then x else y
f :: (Ord a, Num a) => a -> a -> a
-- 引数yにアクセスされなければエラーが生じない
λ> f 1 (2 `div` 0)
1
it :: Integral a => a
λ> f 0 (2 `div` 0)
*** Exception: divide by zero
-- bang patternを利用した関数の定義: call by value
λ> g !x !y = if x > 0 then x else y
g :: (Ord a, Num a) => a -> a -> a
-- 引数x, yは直ちに評価される
λ> g 1 (2 `div` 0)
*** Exception: divide by zero
95
96.
{- Haskell -}
--無限に続く整数のリストから先頭3要素を取り出す
λ> take 3 [0..]
[0,1,2]
it :: (Num a, Enum a) => [a]
-- 3番目の要素にアクセスしなければエラーが生じない
λ> [1, 2, 3 `div` 0] !! 2
*** Exception: divide by zero
λ> take 2 [1, 2, 3 `div` 0]
[1,2]
96
97.
{- Haskell -}
--データ型の定義
λ> data Pair = Pair { l :: Int, r :: Int }
...
-- r にアクセスしなければエラーが生じない
λ> l $ Pair 1 (2 `div` 0)
1
it :: Int
λ> r $ Pair 1 (2 `div` 0)
*** Exception: divide by zero
-- 値コンストラクタに正格性フラグを利用したデータ型の定義
λ> data Pair' = Pair' { l :: !Int, r :: !Int }
...
-- r にアクセスしなくてもエラーが生じる
λ> l $ Pair' 1 (2 `div` 0)
*** Exception: divide by zero
97
98.
/* Scala: 言語の評価戦略は積極(正格)評価*/
// 関数の定義: call by value
scala> def f(x: Int, y: Int): Int =
| if (x > 0) x else y
|
def f(x: Int, y: Int): Int
// 引数x, yは直ちに評価される
scala> f(1, 2 / 0)
java.lang.ArithmeticException: / by zero
// 名前渡しパラメータを利用した関数の定義: call by name
scala> def g(x: => Int, y: => Int): Int =
| if (x > 0) x else y // この場合、xは最大2回評価される
| // (回避するには評価結果をローカル束縛(メモ化)して再利用)
def g(x: => Int, y: => Int): Int
// 引数yにアクセスされなければエラーが生じない
scala> g(1, 2 / 0)
val res1: Int = 1
scala> g(0, 2 / 0)
java.lang.ArithmeticException: / by zero
98
## Elixir: 言語の評価戦略は積極(正格)評価
#関数の定義: call by value
iex(1)> defmodule Some do
...(1)> def f(x, y) do
...(1)> if x > 0, do: x, else: y
...(1)> end
...(1)> end
{:module, Some, ...}
# 引数x, yは直ちに評価される
iex(2)> Some.f(1, 2 / 0)
** (ArithmeticError) bad argument in arithmetic expression...
103
104.
## Elixir
# マクロの定義:call by name
iex(3)> defmodule Other do
...(3)> defmacro g(x, y) do
...(3)> quote do
...(3)> if unquote(x) > 0, # この場合、xは最大2回評価される
...(3)> do: unquote(x), else: unquote(y)
...(3)> end
...(3)> end # (回避するには評価結果をローカル束縛(メモ化)して再利用)
...(3)> end
{:module, Other, ...}
iex(4)> require Other
Other
# 引数yにアクセスされなければエラーが生じない
iex(5)> Other.g(1, 2 / 0)
1
iex(6)> Other.g(0, 2 / 0)
** (ArithmeticError) bad argument in arithmetic expression...
104
105.
## Elixir
# 無限に続く整数のストリームから先頭3要素を取り出す
iex(7)>Enum.take(Stream.from_index, 3)
[0, 1, 2]
# 3番目の要素にアクセスしなければエラーが生じない
iex(8)> 1..3 |> Stream.map(fn n -> if n < 3, do: n, else:
n / 0 end) |> Enum.at(2)
** (ArithmeticError) bad argument in arithmetic expression...
iex(9)> 1..3 |> Stream.map(fn n -> if n < 3, do: n, else:
n / 0 end) |> Enum.take(2)
[1, 2]
105
アドホック多相に関する仕組み
{- Haskell -}
λ>:{
λ| class Solid a where -- 型クラスの定義
λ| surfaceArea :: a -> Double
λ| volume :: a -> Double
λ| :}
type Solid :: Constraint
λ> :{
λ| data Cuboid = Cuboid
λ| { a :: !Double
λ| , b :: !Double
λ| , c :: !Double }
λ|
λ| data Sphere = Sphere { r :: !Double }
λ| :}
type Cuboid :: *
...
115
116.
{- Haskell -}
λ>:{
λ| instance Solid Cuboid where -- 型クラスのインスタンスの定義
λ| surfaceArea (Cuboid a b c) =
λ| 2 * (a * b + b * c + c * a)
λ| volume (Cuboid a b c) =
λ| a * b * c
λ|
λ| instance Solid Sphere where
λ| surfaceArea (Sphere r) =
λ| 4 * pi * r ^ 2
λ| volume (Sphere r) =
λ| 4 / 3 * pi * r ^ 3
λ| :}
116
117.
{- Haskell -}
λ>import qualified Data.Map as M
λ> :{
λ| solidProps :: Solid a => a -> M.Map String Double
λ| solidProps x = M.fromList
λ| [("surface area", surfaceArea x), ("volume", volume x)]
λ| :}
solidProps :: Solid a => a -> M.Map String Double
λ> solidProps $ Cuboid 2 3 4
fromList [("surface area",52.0),("volume",24.0)]
it :: M.Map String Double
λ> solidProps $ Sphere 2
fromList [("surface area",50.26548245743669),("volume",
33.510321638291124)]
it :: M.Map String Double
117
118.
/* Scala */
scala>trait Solid2[A]: // 型クラスの定義
| extension (a: A)
| def surfaceArea: Double
| def volume: Double
|
// defined trait Solid2
scala> case class Cuboid2(
| a: Double,
| b: Double,
| c: Double,
| )
// defined case class Cuboid2
scala> case class Sphere2(r: Double)
// defined case class Sphere2
118
;;; Clojure
user=> (extend-protocolSolid ; プロトコルの実装
Cuboid
(surface-area [{:keys [a b c]}]
(* 2 (+ (* a b)
(* b c)
(* c a))))
(volume [{:keys [a b c]}]
(* a b c))
Sphere
(surface-area [{:keys [r]}]
(* 4 Math/PI (Math/pow r 2)))
(volume [{:keys [r]}]
(* 4/3 Math/PI (Math/pow r 3))))
nil
122
## Elixir
iex(1)> defprotocolSolid do # プロトコルの定義
...(1)> def surface_area(x)
...(1)> def volume(x)
...(1)> end
{:module, Solid, ...}
iex(2)> defmodule Cuboid do
...(2)> defstruct [:a, :b, :c]
...(2)> end
{:module, Cuboid, ...}
iex(3)> defmodule Sphere do
...(3)> defstruct [:r]
...(3)> end
{:module, Sphere, ...}
124
125.
## Elixir
iex(4)> defimplSolid, for: Cuboid do # プロトコルの実装
...(4)> def surface_area(%Cuboid{a: a, b: b, c: c}) do
...(4)> 2 * (a * b + b * c + c * a)
...(4)> end
...(4)> def volume(%Cuboid{a: a, b: b, c: c}) do
...(4)> a * b * c
...(4)> end
...(4)> end
{:module, Solid.Cuboid, ...}
iex(5)> defimpl Solid, for: Sphere do
...(5)> def surface_area(%Sphere{r: r}) do
...(5)> 4 * :math.pi() * r ** 2
...(5)> end
...(5)> def volume(%Sphere{r: r}) do
...(5)> 4 / 3 * :math.pi() * r ** 3
...(5)> end
...(5)> end
{:module, Solid.Sphere, ...}
125
パラメータ多相に関する仕組み
{- Haskell -}
λ>:t id -- 関数idの型(type)は?
id :: a -> a -- 型a
λ> id 42 -- idを数値に適用したとき
42
it :: Num a => a -- 型クラスNumの制約付きの型a
λ> :t Just -- 1引数の値コンストラクタJust
Just :: a -> Maybe a
λ> :t Just 42 -- Justを数値に適用したとき
Just 42 :: Num a => Maybe a
λ> :t Nothing -- 0引数の値コンストラクタNothing
Nothing :: Maybe a
-- cf. 型Maybe aの定義(抜粋)
data Maybe a = Nothing | Just a
127
128.
{- Haskell -}
λ>:k Maybe -- 型コンストラクタMaybeのカインド(kind)は?
Maybe :: * -> * -- 型レベルでの関数に相当
λ> :k Num a => Maybe a -- Maybeを型aに適用したとき
Num a => Maybe a :: * -- 型レベルでの値に相当
λ> :k Functor -- 型クラスFunctor
Functor :: (* -> *) -> Constraint -- 型レベルでの高階関数に相当
λ> :k Functor Maybe -- FunctorをMaybeに適用したとき
Functor Maybe :: Constraint
-- cf. 型クラスFunctorの定義(抜粋)
class Functor f where
fmap :: (a -> b) -> f a -> f b
128
/* Scala */
scala>[A] => (x: A) => identity(x)
val res0: [A] => (x: A) => A = Lambda/0x00002000015018d8@3...
scala> identity(42)
val res1: Int = 42
scala> [A] => (x: A) => Some(x)
val res2: [A] => (x: A) => Some[A] = Lambda/0x000020000150...
scala> val x: Option[Int] = Some(42) // SomeはOptionの派生型
val x: Option[Int] = Some(42)
scala> val y: Option[Int] = None // NoneはOptionの派生型
val y: Option[Int] = None
scala> val z: Option[Any] = x // 型パラメータAは共変(covariant)
val z: Option[Any] = Some(42)
// cf. 関数identityの定義(抜粋)
def identity[A](x: A): A = x
// cf. 型Option[+A]の定義(抜粋)
sealed abstract class Option[+A] extends IterableOnce[A]
with Product with Serializable
final case class Some[+A](value: A) extends Option[A]
case object None extends Option[Nothing]
130
131.
/* Scala */
//型クラスFunctorの定義
scala> trait Functor[F[_]]:
| extension [A](x: F[A])
| def fmap[B](f: A => B): F[B]
|
// defined trait Functor
// Optionに対するインスタンスの定義
scala> given Functor[Option] with
| extension [A](x: Option[A])
| def fmap[B](f: A => B): Option[B] =
| x.map(f)
|
// defined object given_Functor_Option
scala> (x, y)
val res3: (Option[Int], Option[Int]) = (Some(42),None)
scala> x.fmap(_ + 1)
val res4: Option[Int] = Some(43)
scala> y.fmap(_ + 1)
val res5: Option[Int] = None
131