我正在嘗試使用monadic返回類型來做可變參數函數,其參數也需要monadic上下文。 (我不知道如何來形容第二點:如printf
可以返回IO()
但它的不同之處在於它是否最終被IO()
或String
其參數將被視爲相同)Haskell:如何使用monadic上下文編寫單變量可變參數函數
基本上,我有一個數據構造函數需要兩個參數Char
。我想提供兩個指針樣式ID Char
參數,它們可以通過一個類型實例從一個封閉的State
monad自動解碼。所以,而不是做get >>= \s -> foo1adic (Constructor (idGet s id1) (idGet s id2))
,我想做fooVariadic Constructor id1 id2
。
接下來是我到目前爲止所知道的,Literate Haskell風格,以防有人想要複製它並混淆它。
一,基本環境:
> {-# LANGUAGE FlexibleContexts #-}
> {-# LANGUAGE FlexibleInstances #-}
> {-# LANGUAGE MultiParamTypeClasses #-}
> import Control.Monad.Trans.State
> data Foo = Foo0
> | Foo1 Char
> | Foo2 Bool Char
> | Foo3 Char Bool Char
> deriving Show
> type Env = (String,[Bool])
> newtype ID a = ID {unID :: Int}
> deriving Show
> class InEnv a where envGet :: Env -> ID a -> a
> instance InEnv Char where envGet (s,_) i = s !! unID i
> instance InEnv Bool where envGet (_,b) i = b !! unID i
爲了方便一些測試數據:
> cid :: ID Char
> cid = ID 1
> bid :: ID Bool
> bid = ID 2
> env :: Env
> env = ("xy", map (==1) [0,0,1])
我有了這個非一元的版本,它只是需要將環境作爲第一參數。這工作正常,但它不是我所追求的。例子:
$ mkFoo env Foo0 :: Foo
Foo0
$ mkFoo env Foo3 cid bid cid :: Foo
Foo3 'y' True 'y'
(我可以用函數依賴或類型的家庭擺脫需要對:: Foo
類型註釋現在我不大驚小怪了,因爲這不是我的興趣。無論如何。)
> mkFoo :: VarC a b => Env -> a -> b
> mkFoo = variadic
>
> class VarC r1 r2 where
> variadic :: Env -> r1 -> r2
>
> -- Take the partially applied constructor, turn it into one that takes an ID
> -- by using the given state.
> instance (InEnv a, VarC r1 r2) => VarC (a -> r1) (ID a -> r2) where
> variadic e f = \aid -> variadic e (f (envGet e aid))
>
> instance VarC Foo Foo where
> variadic _ = id
現在,我想要一個可變函數在下面的monad中運行。
> type MyState = State Env
基本上,我不知道該怎麼做。我試過用不同的方式表達類型類(variadicM :: r1 -> r2
和variadicM :: r1 -> MyState r2
),但我沒有成功編寫實例。我也嘗試過適應上面的非單調解決方案,以便我以某種方式「結束」Env -> Foo
,然後我可以很容易地變成MyState Foo
,但是沒有運氣。
以下是我迄今爲止的最佳嘗試。
> mkFooM :: VarMC r1 r2 => r1 -> r2
> mkFooM = variadicM
>
> class VarMC r1 r2 where
> variadicM :: r1 -> r2
>
> -- I don't like this instance because it requires doing a "get" at each
> -- stage. I'd like to do it only once, at the start of the whole computation
> -- chain (ideally in mkFooM), but I don't know how to tie it all together.
> instance (InEnv a, VarMC r1 r2) => VarMC (a -> r1) (ID a -> MyState r2) where
> variadicM f = \aid -> get >>= \e -> return$ variadicM (f (envGet e aid))
>
> instance VarMC Foo Foo where
> variadicM = id
>
> instance VarMC Foo (MyState Foo) where
> variadicM = return
它適用於Foo0和Foo1,但從未被超越的是:
$ flip evalState env (variadicM Foo1 cid :: MyState Foo)
Foo1 'y'
$ flip evalState env (variadicM Foo2 cid bid :: MyState Foo)
No instance for (VarMC (Bool -> Char -> Foo)
(ID Bool -> ID Char -> MyState Foo))
(在這裏我想擺脫的需要進行標註,但事實證明,這一提法需要兩個實例對於Foo
使這個問題。)
我瞭解投訴:我只有一個實例,從Bool -> Char -> Foo
到ID Bool -> MyState (ID Char -> Foo)
。但我不能讓它的 實例,因爲我需要MyState
在那裏,以便我可以 將ID Bool
變成Bool
。
我不知道我是完全偏離軌道還是什麼。我知道我可以用不同的方式解決我的基本問題(我不想污染我的代碼,其代碼爲idGet s
),例如爲不同數量的ID參數創建liftA
/liftM
樣式函數,像(a -> b -> ... -> z -> ret) -> ID a -> ID b -> ... -> ID z -> MyState ret
,但我花了太多時間思考這個問題。 :-)我想知道這個可變參數解決方案應該是什麼樣子。
由於您明確不想查找「Applicative」解決方案,因此我將其添加到評論中:https://gist.github.com/f8e5d1ecf20ea09a8b36 –