Как предоставить универсальный тип для метода государственной монады - PullRequest
1 голос
/ 17 мая 2019

Я пытаюсь создать function для монады состояния, которая может получить универсальный тип в a:
newtype State s a=State { runstate::s->(s,a) }

Я хочу связать только первый типrunstate::s->(s,a), а затем решите, что a должно быть:

Поэтому вместо чего-то вроде:

f::Int->(Int,String)
f x | x>3 = (x, "bigger then 3")
    |otherwise =(x,"smaller then 3")

make::State Int String
make =State f

Как я могу выполнить:

makeFree::State Int a
makeFree=State ....

Выможет быть, интересно, зачем мне это нужно.Хотя я знаю тип state, я хочу, чтобы результат вычислений менялся.

1 Ответ

3 голосов
/ 17 мая 2019

Не совсем понятно, что вы имеете в виду, но, основываясь на комментариях, вот несколько предложений.

Параметрическая функция

Как дано, f возвращает (Int, String), и вы не можете просто изменить это. Тем не менее, вы можете настроить его следующим образом:

f' :: a -> a -> Int -> (Int, a)
f' bigger smaller x | x > 3 = (x, bigger)
                    | otherwise = (x, smaller)

Этот вариант больше не возвращает (Int, String), а скорее (Int, a). Однако цена, которую вы платите, заключается в том, что вы должны предоставить bigger и smaller в качестве аргументов. Мы вернемся к этому через мгновение, но прежде чем мы это сделаем, мы можем превратить любую функцию с общим типом s -> (s, a) в значение State s a:

make' :: (s -> (s, a)) -> State s a
make' fn = State fn

Теперь вы можете частично применить f' для изменения типа:

*Q56181862> :t make' $ f' "bigger than 3" "smaller than 3"
make' $ f' "bigger than 3" "smaller than 3" :: State Int [Char]
*Q56181862> :t make' $ f' True False
make' $ f' True False :: State Int Bool

В первом из приведенных выше примеров GHCi типом является State Int String, тогда как второй пример имеет тип State Int Bool.

Возвращение

Из других комментариев кажется, что вы хотите варьироваться от State Int String до State Int (IO String). Хотя вы можете добиться этого с помощью описанной выше техники, вы также можете использовать return в самой функции:

f'' :: Monad m => Int -> (Int, m String)
f'' x | x > 3 = (x, return "bigger than 3")
      | otherwise = (x, return "smaller than 3")

Это меняет только монаду, но не «тип возврата» String. Вы можете предоставить достаточно подсказок системе типов Haskell, чтобы сообщить, что m должно быть IO:

*Q56181862> :t make' $ f'' :: State Int (IO String)
make' $ f'' :: State Int (IO String) :: State Int (IO String)

Если вы не хотите запускать вычисления в IO, вы можете вместо этого запустить их в Identity, что также является Monad:

*Q56181862 Control.Monad.Identity> :t make' $ f'' :: State Int (Identity String)
make' $ f'' :: State Int (Identity String) :: State Int (Identity String)

Теперь вы можете запустить вычисление и извлечь String из Identity, используя runIdentity.

Functor

Если вы также сделаете State s функтором, вы можете извлечь String из Identity:

*Q56181862 Control.Monad.Identity> :t fmap runIdentity $ make' $ f''
fmap runIdentity $ make' $ f'' :: State Int String

Самый простой способ сделать это - использовать расширение DeriveFunctor GHC:

newtype State s a = State { runstate :: s -> (s, a) } deriving Functor

... или вы можете просто использовать Control.Monad.State.Lazy из пакета mtl ...

...