haskell アドホック多相

アドホック多相の意味がわかったかも

今までパラメータ多相でf x=x+1を型宣言しても以下の様にエラーが出ていた

Prelude> let f::a->a ; f x=x+1

<interactive>:1:21:
    Could not deduce (Num a) from the context ()
      arising from the literal `1' at <interactive>:1:21
    Possible fix:
      add (Num a) to the context of the type signature for `f'
    In the second argument of `(+)', namely `1'
    In the expression: x + 1
    In the definition of `f': f x = x + 1

実はこれは(+)が Int,Integer,Float,Doubleをインスタンスとして持つNumクラスでaの制約として働いている。

Prelude> :t (+)
(+) :: Num a => a -> a -> a

だから (+) は Charじゃ使えないんだよな。


ここで fを引数をそのままとして返す関数をつくって制約について実験していく

Prelude> let f::a->a; f x=x in f 1
1
it :: Integer
Prelude> let f::a->a; f x=x in f "a"
"a"
it :: [Char]
Prelude> let f::a->a; f x=x in f 'a'
'a'
it :: Char

パラメータ多相ではどんな型の引数も受け付けている。

Prelude> let f::Num a=>a->a; f x=x in f 1
1
it :: Integer
Prelude> let f::Num a=>a->a; f x=x in f 1.0
1.0
it :: Double
Prelude> let f::Num a=>a->a; f x=x in f 'a'

<interactive>:1:30:
    No instance for (Num Char)
      arising from a use of `f' at <interactive>:1:30-34
(略)

Numクラスで制約を行うとCharでははじくようになる


同様に Integralクラスで制約を行うと Doubleをはじくようになる

Prelude> let f::Integral a=>a->a; f x=x in f 1
1
it :: Integer
Prelude> let f::Integral a=>a->a; f x=x in f (1::Int)
1
it :: Int
Prelude> let f::Integral a=>a->a; f x=x in f (1::Integer)
1
it :: Integer
Prelude> let f::Integral a=>a->a; f x=x in f 1.0

<interactive>:1:35:
    Ambiguous type variable `t' in the constraints:
      `Integral t' arising from a use of `f' at <interactive>:1:35-39
      `Fractional t'

だから IntegralクラスのメソッドであるmodとFractionalクラスのメソッドである(/)は通常では共存できない

Prelude> let f x y= (mod x y==0)&&(x/y<10) in f 6 3

<interactive>:1:38:
    Ambiguous type variable `t' in the constraints:
      `Fractional t' arising from a use of `f' at <interactive>:1:38-42
      `Integral t' arising from a use of `f' at <interactive>:1:38-42
    Probable fix: add a type signature that fixes these type variable(s)

ここでfromIntegralの以下の性質を利用して Num型に引き上げる

Prelude> :t fromIntegral (3::Integer)
fromIntegral (3::Integer) :: Num b => b

こうすることで/が使えるようになる。

Prelude> let f::Integral a=>a->a->Bool; f x y=  mod x y==0 && (fromIntegral x)/(fromIntegral y)< 10 in f 6 3
True
it :: Bool


たぶんこの理解であってると思う。

だから一番最初のやつは

Prelude> let f::Num a=>a->a ; f x=x+1 in f 2
3
it :: Integer

でいいとおもう。