質問 1: ラムダは s (状態) をパラメーターとしてどのように取り込んでいますか? どのように渡されますか?
次のように定義されたget
との古典的な定義を使用しましょう。put
put :: Int -> SimpleState ()
put n = SimpleState (\_ -> ((), n))
get :: SimpleState Int
get = SimpleState (\s -> (s, s))
を呼び出すと、型の関数を公開するapplySimple
をアンラップします。次に、その関数を初期状態に適用します。いくつかの具体的な例を使って試してみましょう。SimpleState
Int -> (a, Int)
まず、コマンドを実行します。put 1
初期状態は次の0
とおりです。
applySimple (put 1) 0
-- Substitute in definition of 'put'
= applySimple (SimpleState (\_ -> ((), 1))) 0
-- applySimple (Simple f) = f
(\_ -> ((), 1)) 0
-- Apply the function
= ((), 1)
put
が初期状態を無視し、右側の状態スロットを単に に置き換え、左側の戻り値スロットを1
残していることに注目してください。()
次に、開始状態を使用して get コマンドを実行してみましょう0
:
applySimple get 0
-- Substitute in definition of 'get'
= applySimple (SimpleState (\s -> (s, s))) 0
-- applySimple (SimpleState f) = f
= (\s -> (s, s)) 0
-- Apply the function
= (0, 0)
get
左の戻り値スロットにコピー0
するだけで、右の状態スロットは変更されません。
SimpleState
したがって、初期状態をそのラムダ関数に渡す方法は、 newtype をアンラップして基礎となるラムダ関数を公開し、ラムダ関数を初期状態に直接適用するだけです。
質問 2: applySimple が関数シグネチャで 1 つのパラメーターを使用している場合、なぜラムダ内に applySimple st があるのですか? applySimpleapplied が 2 回適用されるのはなぜですか?
これは、applySimple
のタイプが ではないためInt -> (a, Int)
です。それは実際には:
applySimple :: SimpleState -> Int -> (a, Int)
これは、Haskell のレコード構文の紛らわしい側面です。次のようなレコードフィールドがあるときはいつでも:
data SomeType { field :: FieldType }
... thenfield
のタイプは実際には次のとおりです。
field :: SomeType -> FieldType
私はそれが奇妙であることを知っています。
質問 3. これは何ですか? SimpleState に対して何らかのアクションを実行しているのに、その署名が関数ではないのはなぜですか?
SimpleState
newtype は、ラップする関数を隠します 。newtypes
アンラップするまで、何でも隠すことができます。あなたSimpleState
はその中に関数を持っていますが、あなたがそれをアンラップするまで、コンパイラが見るのは外側の newtype だけですapplySimple
。
質問 4: >>= で tic を使用できますか / どのように使用しますか?
の定義を間違えていますincr
。正しい使い方tic
は次のようになります。
ticTwice :: SimpleState ()
ticTwice = do
tic
tic
...コンパイラはこれを次のように変換します:
ticTwice =
tic >>= \_ ->
tic
および ticの定義を使用して(>>=)
、これにより値が 2 増加することを証明できます。
tic >>= \_ -> tic
-- Substitute in definition of `(>>=)`
SimpleState (\s ->
let (x, s') = applySimple tic s
in applySimple ((\_ -> tic) x) s')
-- Apply the (\_ -> tic) function
SimpleState (\s ->
let (x, s') = applySimple tic s
in applySimple tic s')
-- Substitute in definition of `tic`
SimpleState (\s ->
let (x, s') = applySimple (SimpleState (\s -> (s, s + 1))) s
in applySimple (SimpleState (\s -> (s, s + 1))) s'
-- applySimple (SimpleState f) = f
SimpleState (\s ->
let (x, s') = (\s -> (s, s + 1)) s
in (\s -> (s, s + 1)) s'
-- Apply both functions
SimpleState (\s ->
let (x, s') = (s, s + 1)
in (s', s' + 1)
-- Simplify by getting rid of unused 'x'
SimpleState (\s ->
let s' = s + 1
in (s', s' + 1)
-- Simplify some more:
SimpleState (\s -> s + 1, s + 2)
したがって、 をtic
使用して 2 つの を連鎖させると、状態を だけインクリメントし、状態プラスを返す(>>=)
単一のステートフル関数にそれらを結合することがわかります。2
1