そのやり方をしたら今度は
どうすれば・・・
reply b msg
の部分で型推論できなくてエラーになっちゃいましたorzどうすれば・・・
reply b msg
の部分で型推論できなくてエラーになっちゃいましたorzdata AnyBot env = forall b. (Bot b, Dep b env) => AnyBot b instance Bot (AnyBot env) where type Dep (AnyBot env) a = (a ~ env) name (AnyBot b) = name b reply (AnyBot b) msg = reply b msg
Generic
や NFData
(あと、多分テストで使う都合で Eq
や Show
も)のインスタンスにしておいた方がいいでしょうか?Bot
のリストとかも Bot
のインスタンスにしたいなと思って以下のようにインスタンス宣言を追加したら、またどうしていいかわからないエラーが出ちゃいました:sob::x::one::zero::zero::zero:instance (Bot (b (env :: *)), Dep (b env) env, Functor t, Traversable t) => Bot (t (b env)) where type Dep (t (b env)) a = a ~ env name _ = "Bot Pool" reply bs msg = join . L.find isJust <$> traverse (`reply` msg) bs
Conflicting family instance declarations: Dep (AnyBot env) a = a ~ env -- Defined at src/Bot.hs:49:8 Dep (t (b env)) a = a ~ env -- Defined at src/Bot.hs:54:
AnyBot
を Bot
のインスタンスにするのをやめて、`AnyBot` を Functor
と Traversable
にしちゃうのがいいんでしょうか…?Bot
のインスタンスにしたい」とのことなので、具体的にどんな型をインスタンスにしたいか列挙して、それらがどう実装できるかを試してみるのが良いと思いますinstance Bot b => Bot [b]
instance Bot b => Bot (Vector b)
instance Bot b => Bot [b]
まさにその通りです。`instance Applicative f => MyClass f`のような宣言はあらゆる型に対してインスタンスを定義することになるので、多くの場合はアンチパターンですclass Summable a where sum :: a -> a
に対して instance (Foldable f, Num a) => Summable (f a) where sum = foldl' (+) 0
という一般的な定義をしても、それは最初から型クラスを使わずに sum = foldl' (+) 0
と定義するのと変わらないのですallBots :: HasLogFunc e => [AnyBot e] allBots = [AnyBot MarkovChain, AnyBot Shiritori] allBotNames :: forall e. HasLogFunc e => [String] allBotNames = map name (allBots :: [AnyBot e])
allBotNames
を呼び出す方法がわからないです:sob:allBots :: [AnyBot e] allBots = [AnyBot MarkovChain, AnyBot Shiritori] allBotNames :: [String] allBotNames = map name (allBots :: [AnyBot ()])
Dep (AnyBot env) env
が成立することが必要な気がしますが)Bot
のインスタンスにすることではなく、全てのボットを同じ型で管理して、名前が取れることだけだったりしないですかね?その場合、instance Bot (AnyBot env)
allBots :: [AnyBot e] allBots = [AnyBot MarkovChain, AnyBot Shiritori] allBotNames :: [String] allBotNames = map (\(AnyBot b) -> name b) (allBots :: [AnyBot ()])
Dep b env
を要求してるからか...instance HasLogFunc Logger
Logger
があるなら、一応はallBotNames :: [String] allBotNames = map name (allBots :: [AnyBot Logger])
data Bot = Bot { name :: String , reply :: HasLogFunc e => String -> RIO env (Maybe String) } markovChain :: Bot markovChain = Bot { ... } shiritori :: Bot shiritori = Bot { ... } anyBots :: [Bot] anyBots = [markovChain, shiritori] allBotNames :: [String] allBotNames = map name allBots
instance HasLogFunc LogFunc
allBotNames :: [String] allBotNames = map name (allBots :: [AnyBot LogFunc])
LogFunc
が出てくるのはなんかすごく変な感じします:sob:allBots :: [AnyBot e] allBots = [AnyBot MarkovChain, AnyBot Shiritori]
AnyBot MarkovChain
のところで必要な HasLogFunc
がなくてエラーになっちゃいます:sob:e
も存在型で包んでしまう、ですかねぇ(本当にそれで問題ないのか、他に問題が発生しないか自信がないですname
を呼び出すのに env
に関する制約が必要なかったのに,`AnyBot` に関しては必要になるところが問題なんですかね?元々のやり方を踏襲するなら,基本的にはdata AnyBot env = forall b. (Bot b, Dep b env) => AnyBot b instance Bot (AnyBot env) where type Dep (AnyBot env) a = (a ~ env) name (AnyBot b) = name b reply (AnyBot b) msg = reply b msg
AnyBot
の定義とインスタンス定義が問題なんだと思いますね.`Bot` クラスの Dep
は本来,`reply` に必要な env
の制約を表してたんだと思いますが,それが AnyBot
の pack 時に押し付けられて,`AnyBot` に対する Dep
は型合わせのための制約しか入ってないのが問題なんだと思いますMarkovChain
/ Shiritori
などはユーザが定義するものですが,`AnyBot` はその定義されたボットを使う側,つまりボットサーバの実装側が定義するものだと思うので,別に env
が多相的になっていなくても問題ないと思いますね.なので,実装側で使う AppEnv
のような型に対して,data AppBot = forall b. (Bot b, Dep b AppEnv) => AppBot b instance Bot AppBot where type Dep AppBot env = env ~ AppEnv name (AppBot b) = name b reply (AppBot b) msg = reply b msg
e
も存在型にってどうするんでしょうallBots
を定義するときには [AnyBot MarkovChain, AnyBot Shiritori]
などと列挙するので、それらが必要としてる`env`の制約を書くのを回避する必要はあまりなさそうですね:owl:TypeApplications
ならできるんですね!