Haskell/confused

Haskell のコードを読んでいて, 読み方 (脳内 parse) や意味が分からなかったところをメモしていきます.

関数の呼び出し括弧を書かない

単に自分の慣れの問題です. 自分は呼び出し括弧を必ず書く方が好きです.

演算子が多過ぎ

似たようなのがあったり, 記号だけで書こうとし過ぎていて, どんな演算だったかを覚え切れないです.

定義の右辺に未定義語が出てくる

これまでの経験から, 左辺にあるのが定義したいもので, 右辺に出てくるのは定義で使われる定義済みのものだと考えてしまって混乱します.

代数的データ型の定義のことです.

data  Maybe a  =  Nothing | Just a
  deriving (Eq, Ord)

これを見たときに「Just の定義はどこだ?」と探しても見付からず混乱してました. (実際には, まさにここが Just の定義されている場所だったのですが……)

(https://hackage.haskell.org/package/base-4.8.1.0/docs/src/GHC.Base.html#Maybe より引用)

型変数のある data 宣言の文法に違和感

data  Maybe a  =  Nothing | Just a
  deriving (Eq, Ord)

またこの例を出しますが, 型変数 a の意味合いが左辺と右辺で異なっています.

左辺ではカインド * * を持つ型コンストラクタ Maybe の仮引数の名前として登場し, 右辺では型 a Maybe a を持つデータコンストラクタ Just の引数の型として登場しています. 左辺の Maybe a という並びと右辺の Just a という並びを見たときに a の意味合いが変わるのは, ちょっと違和感があります.

(data宣言の解説はここ → https://wiki.haskell.org/Type#Data_declarations)

型クラスのインスタンスのカインドが分かりづらい

class  Functor f  where
    fmap        :: (a -> b) -> f a -> f b

    -- | Replace all locations in the input with the same value.
    -- The default definition is @'fmap' . 'const'@, but this may be
    -- overridden with a more efficient version.
    (<$)        :: a -> f b -> f a
    (<$)        =  fmap . const

(https://hackage.haskell.org/package/base-4.8.1.0/docs/src/GHC.Base.html#Functor より引用)

instance  Functor Maybe  where
    fmap _ Nothing       = Nothing
    fmap f (Just a)      = Just (f a)

(https://hackage.haskell.org/package/base-4.8.1.0/docs/src/GHC.Base.html#line-629 より引用)

この FunctorMaybe の定義から Functor のインスタンス (ここの例では Maybe) が持つカインドが * * を持つことは分かりません.

data  Maybe a  =  Nothing | Just a
  deriving (Eq, Ord)

を見てようやく Maybe のカインドが分かります.

ちなみにghci上でそれぞれのカインドを確認するとこうなります.

Prelude> :k Functor
Functor :: (* -> *) -> Constraint

Prelude> :k Maybe
Maybe :: * -> *

Prelude> :t Just
Just :: a -> Maybe a

Prelude> :info Functor
class Functor (f :: * -> *) where
  fmap :: (a -> b) -> f a -> f b
  (<$) :: a -> f b -> f a
  {-# MINIMAL fmap #-}
    -- Defined in ‘GHC.Base’
instance Functor (Either a) -- Defined in ‘Data.Either’
instance Functor [] -- Defined in ‘GHC.Base’
instance Functor Maybe -- Defined in ‘GHC.Base’
instance Functor IO -- Defined in ‘GHC.Base’
instance Functor ((->) r) -- Defined in ‘GHC.Base’
instance Functor ((,) a) -- Defined in ‘GHC.Base’

これで分かるっちゃ分かるんですが, 定義している箇所で明示的に書かれないのには違和感があります.

Monadreturn という名前

なぜ Monad に付随する型 Monad m => a -> m a を持つ関数の名前が return なのかサッパリ分かりませんでした.

検索してみると Why is the return function called return? という記事があって, Haskellerの人達も納得してるわけではないようです.

(Monad m) => みたいな表記

例えばここで使われている表記です.

join              :: (Monad m) => m (m a) -> m a
join x            =  x >>= id

(https://hackage.haskell.org/package/base-4.8.1.0/docs/src/GHC.Base.html#line-426 より引用)

Type constraints (型制約) と呼ぶそうです.

型変数 m の「型」に相当する型クラス Monad を前置するのに違和感があります. 型と型クラスが違うのは分かりますが, 一貫性が無いように見えてしまいます.

=> という記号も -> という記号に似ていますが「型クラス変数 m の宣言」みたいな発想なのでしょうか?

HaskellのREPL

PythonのREPLを基準に考えてしまうので, HaskellのREPLで困惑したところがあります. PythonのREPLでは, そこに打ち込んだコードをそのままコピペしてスクリプトファイルが作れます. それを期待してしまっていたので, HaskellのREPLでは戸惑ってしまいました.

学び始めの頃は, 変数定義に let が必要な理由も, 単にファイルに書くときと同じ構文 (例えば関数の型宣言) が使えない理由も分からなかったです. 理由は do 構文の内部だかららしいです. http://stackoverflow.com/questions/14052093/ghci-let-what-does-it-do