0

私は現在 Writer モナドについて学んでいますが、doブロック内のモナドの値とアキュムレータの両方を読み取ることが正しいかどうかはわかりません。たとえば、以下の coltzSeq 関数では、配列アキュムレータの長さを関数の最終計算として読み取りたいと考えています。これを行うことは可能ですか、またはこれは Writer の不適切な使用ですか? 明らかに、呼び出し元に最終的な配列の長さを読み取らせるか、State モナドを使用することができますが、これは単なる演習です。

module Main where

import Prelude
import Data.Tuple
import Control.Monad.Writer
import Math (remainder, (%))
import Data.Int (toNumber, fromNumber)
import Control.Monad.Eff.Console (logShow)

coltz :: Number -> Number
coltz n = case (n % 2.0 == 0.0) of
            true  -> n / 2.0
            false -> 3.0 * n + 1.0

coltzW :: Number -> Writer (Array Number) Number
coltzW n = do
  tell [n]
  pure $ coltz n

-- Computes a coltz sequence and counts how many
-- steps it took to compute
coltzSeq :: Number -> Writer (Array Number) Int
coltzSeq n = do

  -- Can read the value in the writer
  -- but not the writer's internal state
  v <- (coltzW n)
  let l = 1

  -- Can read the value and the internal
  -- state, but it's not bound to the monad's context.
  -- let a = runWriter (coltzW n)
  -- let v = fst a
  -- let l = length (snd a)
  case (v) of
    1.0 -> pure $ l
    _ -> to1 v

編集: gb. の提案を試し、listenstype を持つ関数を使用してみました(Monoid w, Monad m) => forall w m a b. MonadWriter w m => (w -> b) -> m a -> m (Tuple a b)idこのコンテキストで使用すると、型は次のようになります...

MonadWriter w m => (w -> b) -> m a -> m (Tuple a b)

w = Array Number
m = WriterT (Array Number) Identity (alias: Writer (Array Number) )
b = Array Number
a = Number

(Array Number -> Array Number) ->
Writer (Array Number) Number ->
Writer (Array Number) (Tuple Number (Array Number))

したがって、listens idを受け入れWriter (Array Number) Number)、値が現在の Writer 状態である Writer を返します ( を使用したためid)。ただし、試して使用するすべての方法で型エラーが発生し続けますlistens

to1 :: Number -> (Writer (Array Number)) Int
to1 n = do
  v <- (coltzW n)
  -- a <- snd <$> listens id
  -- let l = snd <$> (listens id (execWriter (coltzW n)))
  -- let l = execWriter (listens id (coltzW n))

  -- Seems like this one should work to get Array Number
  -- let l = snd <$> (listens id (coltzW n))

  case (v) of
    1.0 -> pure 1
    _   -> to1 v

Edit2: 何をする必要があるかを理解しました。何らかの理由で、使用時に型注釈を追加する必要がありましたlistens

lengthOfSeq :: Writer (Array Number) Int -> Writer (Array Number) Int
lengthOfSeq c = do
  -- Without type annotation, I get this error...
  -- No type class instance was found for
  --                                                                             
  -- Control.Monad.Writer.Class.MonadWriter (Array t0)                       
  --                                        (WriterT (Array Number) Identity)
  --                                                                             
  -- The instance head contains unknown type variables. Consider adding a type annotation.
  Tuple a w <- (listens id c :: Writer (Array Number) (Tuple Int (Array Number)))
  pure $ length w

to1 :: Number -> (Writer (Array Number)) Int
to1 n = lengthOfSeq $ seq n
  where
  seq n = do
    v <- coltzW n
    case (v) of
      1.0 -> do
        pure 1
      _   -> seq v
4

2 に答える 2