そのやり方をしたら今度は
どうすれば・・・
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 allBotsinstance 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 ならできるんですね!