haskell-jp / beginners #19 at 2021-11-25 14:16:10 +0900

Endo Ryunosuke / minerva
Bifunctor って第一引数を固定すると Functor になると思うんですが,そういうインスタンス定義は提供されてないみたいなので,
instance Bifunctor f => Functor (f a) where
  fmap = second

と書いてみるとこれはコンパイル通りません.エラーメッセージは
[1 of 1] Compiling Main             ( adv.hs, interpreted )

adv.hs:70:25: error:
    • Illegal instance declaration for 'Functor (f a)'
        (All instance types must be of the form (T a1 ... an)
         where a1 ... an are *distinct type variables*,
         and each type variable appears at most once in the instance head.
         Use FlexibleInstances if you want to disable this.)
    • In the instance declaration for 'Functor (f a)'
   |
70 | instance Bifunctor f => Functor (f a) where
   |                         ^^^^^^^^^^^^^
Failed, no modules loaded.

で,指示通り {-# LANGUAGE FlexibleInstances #-} をつけるとコンパイルは通りますが,デフォルトだとなんでこんな制約があるんでしょう?
Functor (f a) に対してそういうインスタンスを定義してしまうと、具体的な型 F に対する Functor (F a) を定義できなくなります。例えば次のコードがコンパイルエラーになります:
instance Bifunctor f => Functor (f a) where
  fmap = second

data Foo k a = Foo (k -> k) a -- FooはBifunctorではない

instance Functor (Foo k) where
  fmap f (Foo k a) = Foo k (f a)

「`Bifunctor` を利用した Functor のインスタンスを定義したい」のであればnewtypeを使うのがHaskell流のやり方になるかと思います:
newtype WrappedBifunctor f a b = WrappedBifunctor (f a b)
instance Bifunctor f => Functor (WrappedBifunctor f a) where
  fmap f (WrappedBifunctor a) = WrappedBifunctor (second f a)
Endo Ryunosuke / minerva
なるほど!ちょうど instance Functor (RoseTreeF a) where ... とかやって怒られてたところでした.今回の場合,`RoseTreeF` が Bifunctor だということは導けないはずなんですが,GHC はその手の探索(制約解消?)まではやってくれないということなんですね.
残念ながらご認識のとおりです。GHCが型クラスのインスタンスを解決するときは、例えば
instance SomeClass a => SomeClass (SomeType a)

と書いたときの SomeType a という部分でしか探索しません。 SomeClass a が実際に満たされているかは解決した後に確認します。
Endo Ryunosuke / minerva
なるほど、Haskell 的にはそれくらいがちょうどいい落とし所だという認識なのだろうと思うと面白いですね。読みたい: https://people.csail.mit.edu/dnj/teaching/6898/papers/wadler88.pdf