モナドの戻り値の型を持つ可変引数関数を作成しようとしていますが、そのパラメーターにもモナドのコンテキストが必要です。(その 2 番目の点をどのように説明すればよいかわかりません。たとえば、printf
を返すことができますが、最終的にまたはIO ()
であるかどうかに関係なく、そのパラメーターが同じように扱われるという点で異なります。)IO ()
String
基本的に、たとえば 2 つのパラメーターを取るデータ コンストラクターがありChar
ます。代わりに、型クラスのインスタンスを介してID Char
囲んでいるモナドから自動的にデコードできる2 つのポインター スタイルの引数を提供したいと考えています。State
だから、する代わりに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
ここで、次のモナドで実行される可変個引数関数が必要です。
> 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))
(ここでは注釈の必要性を取り除きたいのですが、この定式化には 2 つのインスタンスが必要であるという事実がFoo
問題になります。)
Bool ->
Char -> Foo
私は苦情を理解しています: 私は からへ行くインスタンスしか持っていませんID Bool -> MyState (ID Char -> Foo)
. しかし、 をに変換MyState
できるようにどこかに必要なため、必要なインスタンスを作成できません。ID Bool
Bool
私が完全に軌道から外れているのか、それとも何なのかわかりません。のような型を使用して、さまざまな数の ID パラメーターに対して /スタイルの関数をidGet s
作成するなど、さまざまな方法で基本的な問題 (コードをあらゆる場所で同等のもので汚染したくない) を解決できることを知っていますが、私はこれについて考えるのに多くの時間を費やしました。:-) この可変個引数のソリューションがどのように見えるべきか知りたいです。liftA
liftM
(a -> b -> ... -> z -> ret) -> ID a -> ID b -> ... -> ID z -> MyState ret