haskell勉強
Haskell nix参考資料
ghcが使えるshellを起動
nix-shell -p ghc
3rd packageを使う
nix-shell \
-p "haskellPackages.ghcWithPackages (p: [p.3rdpackge])" \
Stringの代わりにData.Textを使うのがよい?
関数を探す
hoogle.haskell.org
Haskellにおける降順ソート
経験豊富な Haskeller は、まず型クラスを探し、それからコーディングを始めることが多い。
型クラスの調べ方
ghciで
:info typeclass
:doc typeclass
ghc拡張が色々あるのでHaskellを使いこなすにはそれに詳しい必要がある
POLYMORPHIC VALUES
C a => a
型は多相的と呼ばれ、様々な形式で使える。
コードが動かないときは考えられる原因を列挙する
かなり難しく感じるが手続き型言語を初めて勉強したときも同じだったはず。
手続き型言語習熟者が新たな手続き型言語を習得するのと関数型言語を習得するのは難易度が全く違って当たり前
ある程度基礎を学んだあとはなにで入門する?
候補
- CLI
- Webサーバー
- 自作ライブラリ
elmより普通に実行時エラーになる
実行時エラーになる関数がちょくちょくある
headやread等
haskellで評判が悪いもの
- 部分関数 結果が未定義(例外を投げる)引数の組み合わせがある
- 遅延IO
中級者への道
言語拡張を使えるようになる
データ型を型クラスのインスタンスにできるようになる
部分関数を避ける
packageを知る
computational context: functor, applicative, monadのなんとなく理解してコーディングができる
ghciコマンドが使える
hoogleを活用できる
Traversalが使いこなせる
lensを理解する
ReadとShowのissue
Stringを連結して構築するのは非常に非効率的です。Show型クラスはかなり限定的な解決策を与えてくれますが、それについては後ほど説明します。
再帰的データ型には、括弧の問題があります。これについてもShow型クラスで対処しています。
手動で実装されたShowとの互換性を維持するためにReadインスタンスを実装するのは大変です。
計算コンテキスト
Functor
値に対するマッピング fmap
Applicative
コンテキストに値を注入 pure
コンテキスト内の関数をコンテキスト内の値に適用 <*>
Monad
あるコンテキストでの計算を順番に実行し、次の計算で何が行われるか前の計算の結果に依存するようにする >>=
Traversal
traverse :: Applicative f => (a -> f b) -> t a -> f (t b)
構造を保ちながら(t a
からt b
)コンテキストをまとめる
ghci> traverse readMaybe ["1", "2", "3"] :: Maybe [Int]
Just [1,2,3]
stockquotesアプリケーション作成する際のの課題
- データ表現
- CSVファイルのパース
- レポートの書式設定
- グラフの作成
- UI設計
- コマンドライン引数の扱い
- 純粋部とIO部の区分け
- IO部はなるべく小さく
便利で高性能なHaskellライブラリの例
blaze-html
lucid
型クラスのヘビーユースはHaskellで極めて一般的なidiom
ライブラリは型クラスで制約や振る舞いを描写する
ライブラリを使うために型クラスを導出するのは我々の責任
ビルド成功させるのムズくない?気のせい?
haskellにおけるcontainer
HaskellとNix
preludeには非効率なStringや部分関数headなどがあるからcustom preludeを使ったほうがいいかも
- protolude
- relude
- universum
コマンドライン引数のパース
optparse-applicative
Reader モナド
情報を暗黙のうちに渡し、好きなときにアクセスできる効果を持った計算を定義する。これはまさに、Readerモナドが使われていることです。
processLine :: (Int, Text) -> Writer [ErrorMsg] SQL
processLine (_, T.splitOn ":" -> [s1, s2]) = pure $ genInsert s1 s2
processLine (i, s) = tell [WrongFormat i s] >> pure ""
2行目: The ViewPatterns
GHC extension
Writerモナド
データを書き出せる
実用スべきでない?
Stateモナド
Reader + Writer?
勉強したい
traverseに慣れる
applicative
Stateモナドは可変状態を持つ命令形のアルゴリズムの実装を容易にする
monadic parser combinator
extraパッケージ
標準パッケージの強化版
例えば Data.List.ExtraはData.Listを再エクスポートして、さらに関数を追加している
forallって何?
複数のモナドを使いたいときってどうするの?
Monad transformers
例
type EvalM = stateT Stack Maybe
仕組みは分かっていない
多層にしたい場合は?
→入れ子にする
newtype MaybeT m a = MaybeT { runMaybeT :: m (Maybe a) }
MaybeT { runMaybeT :: m (Maybe a) }
はどういう意味?
MaybeT
は値コンストラクタ
runMaybeT
というフィールドを持ちその型は m (Maybe a)
型付穴
instance Applicative m => Applicative (MaybeT m) where
pure :: a -> MaybeT m a
pure a = MaybeT (pure $ Just a)
(<*>) :: MaybeT m (a -> b) -> MaybeT m a -> MaybeT m b
(MaybeT mf) <*> (MaybeT mx) = MaybeT ((<*>) <$> mf <*> mx)
(MaybeT mf) <*> (MaybeT mx) = MaybeT ( [(<*>) <$> mf ] <*> mx )
[(<*>) <$> mf ]
: m (Maybe a -> Maybe b)
mx
: m (Maybe a)
<*>
: m (Maybe a -> Maybe b) -> m (Maybe a) -> m (Maybe b)
型パズルすぎる。。直感的に理解できない😭
持ち上げ
instance MonadTrans MaybeT where
lift :: Monad m => m a -> MaybeT m a
...
Extensible Effects モナド変換子の競合?
詳しく知りたい
MonadFail
実装するとパターンマッチングの失敗に対応できる?
MonadPlus
monad transformer in haskell libarary
transformers
mtl
State
状態を変化させる関数を持つ
s -> (s, a)
Monad Transformer
とりあえず内部実装は置いておいて関数の動作を把握する
Archiver はStateT
evalStateTはStateTと状態をとって、内側のモナドに値aを適用した値を返す
runXXX系 newtypeでwrapしたものを返す
Stateのget モナドの中の現在の状態値を取得する←状態はどこから来る? paserCharの使われ方を見る
put :: Monad m => s -> StateT s m () 状態をsにする
State
runState :: s -> (s, a)
StateT
runStateT :: s -> m (a, s)
liftStateT :: Monad m => m a -> StateT s m a
liftStateT a = StateT $ \s -> (, s) <$> a
あるモナド値をそれを状態としてもつ、StateTにする。
do記法が使われているときに、それがどのモナド下で起こっているかの判断はその関数の戻り値でする?
ExceptT (m (Either e a))
throwError :: Monad m => e -> ExceptT e m a
throwError exception = ExceptT (pure $ Left exception)
あるエラーをそれをLeftにもつExceptT値にする
newtype:内部が関数のものと普通の値のものがある
新たな抽象的概念が登場するたび、以下が必要になる
- 理論を理解する
- 使って慣れる
わかってない
Monad Trasfomerの重ねる順番の挙動への影響
guessTheState :: String -> GuessingGame Bool
guessTheState guess = do
answer <- get
put guess
pure $ guess == answer
遅延評価のメモ化は独特。。
以下の用な感じでメモテーブル作ってlookupで計算していく。遅延評価だからdistanceを定義した段階だとサンクの状態である
distances =
map (\idxA -> map (getEditDistance idxA) [0..bLen]) [0..aLen]