Обновление и возврат нескольких значений с использованием монады состояния - PullRequest
0 голосов
/ 04 января 2019

Я работаю над оценщиком контракта для контракта DSL, который я пишу. Контракт DSL основан на документе финансовых комбинаторов Саймона Пейтона Джонса. Я относительно новичок в Хаскеле и монадах, и у меня проблемы с государственной монадой. Как вы можете видеть ниже, evalAll2 вызывается рекурсивно, пока не будет достигнут конец. У меня есть три переменные в записи ContractSt, которые обновляются при вызове evalC, people - количество сторон договора (идентифицируется уникальным int), остаток - сумма денег в договоре, а владелец - лицо, которому принадлежит контракт. Контракт оценивается так:

bettingContract :: Contract
bettingContract 
          = until (Date (2018,12,13)) 
                (cashIn 20 1 
                    (cashIn 20 2 
                        (time (Date (2018,12,15))
                            (pay 1 2 40 End)
                        End)
                    End)                            
                End)
            End

c1 = evalAll(bettingContract)

Я хочу, чтобы код выводил оцениваемый Контракт, «выход», который представляет собой сообщение, сгенерированное на основе действия конкретного объекта, и отображал значения ContractSt после оценки контракта. Основная проблема в том, что я получаю:

<interactive>:12:1: error:
    * No instance for (Show
                         (Control.Monad.Trans.State.Lazy.StateT
                            ContractSt
                            Data.Functor.Identity.Identity
                            ()))
        arising from a use of `print'

когда я пытаюсь оценить контракт. Я понимаю, что мне нужно использовать evalState для возврата окончательного результата (что я и хочу), и что я не пытаюсь создать экземпляр для show for (), но возвращаю конечное состояние вместе с контрактом, и выходной, где я испытываю наибольшую сложность.

data ContractSt = ContractSt { 
people :: [Person], balance :: Money, owner :: Person } deriving (Show)

evalAll :: Contract -> (Contract, OP, State ContractSt ()
evalAll c = evalAll2 c [] initialState 

evalAll2 :: Contract -> OP -> State ContractSt () -> (Contract, OP, State 
ContractSt ()
evalAll2 c o s
    | c == End = (c, o, s)
    | otherwise = evalAll2 nc (o ++ no) ns
        where
            (nc, no,ns) = evalC c 

1 Ответ

0 голосов
/ 04 января 2019

Я не прочитал вашу мотивацию, но если ваша проблема заключается в том, как преобразовать последний элемент 3-кортежа, вы можете легко написать для этого функцию:

mapLast3 f (a,b,x) = (a, b, f x)

Здесь в полном примере 3 кортежа (Int, Double, State String ()) преобразуется в (Int, Double, String) путем выполнения вычисления с жестко заданным начальным состоянием:

import Control.Monad.State

mapLast3 :: (x -> c) -> (a,b,x) -> (a,b,c)
mapLast3 f (a,b,x) = (a, b, f x)

myExecutor :: State String () -> String
myExecutor f = execState f "Hello"

execLast3 :: (a, b, State String ()) -> (a, b, String)
execLast3 = mapLast3 myExecutor

myStateFunction :: State String ()
myStateFunction = modify (++ " World")

myOriginalTuple :: (Int, Double, State String ())
myOriginalTuple = (42, 3.14, myStateFunction)

myFinalTuple :: (Int, Double, String)
myFinalTuple = execLast3 myOriginalTuple

main = print myFinalTuple

Будет напечатано (42,3.14,"Hello World")

...