在Haskell中,函子几乎总是可以派生的,是否存在类型为函子且满足函子定律(例如fmap id == id
)但不能根据一组简单规则派生的函子?
那可折叠,可遍历,Semigroup等如何呢?有没有重要的案例?
答案 0 :(得分:12)
我偶然发现了一种有趣的论点。 (很晚了,所以我想知道明天是否会有意义)
我们可以将SK可归约性的证明类型构造为GADT:
infixl 9 :%:
data Term = S | K | Term :%: Term
-- small step, you can get from t to t' in one step
data Red1 t t' where
Red1S :: Red1 (S :%: x :%: y :%: z) (x :%: z :%: (y :%: z))
...
现在,让我们创建一个在还原链末端隐藏其功能的类型。
data Red t a where
RedStep :: Red1 t t' -> Red t' a -> Red t a
RedK :: a -> Red K a
RedS :: (a -> Bool) -> Red S a
现在,如果Red t
归一化为Functor
,则t
是一个K
,但是如果归一化为S
则不是RedK
—一个无法确定的问题。因此,也许您仍然可以遵循“简单的规则集”,但是如果您允许GADT,则规则必须足够强大以计算任何内容。
(有一种替代的表达方式,我觉得它很优雅,但可能不那么具示范性:如果删除Red t
构造函数,则Functor
是t
,当且仅当类型系统可以表示infoPopup.showAtLocation(view, Gravity.TOP, 0, (int) view.getY());
的减少量发散 -有时它发散,但您无法证明这一点,我在这种情况下是否确实是函子感到困惑
答案 1 :(得分:9)
在这个问题上,没有非平凡的函子。所有函子都可以作为Either
和Tuple
函子的和(Identity
)和乘积(Const
)进行机械推导。有关此构造的详细信息,请参见关于Functorial Algebraic Data Types的部分。
这在更高的抽象水平上可行,因为Haskell的类型形成Cartesian Closed Category,其中存在终端对象,所有乘积和所有指数。
答案 2 :(得分:9)
可以将空的参数类型明确地自动创建为函子:
data T a deriving Functor
但是,隐式为空的不能:
import Data.Void
data T a = K a (a -> Void)
deriving Functor -- fails
{-
error:
• Can't make a derived instance of ‘Functor T’:
Constructor ‘K’ must not use the type variable in a function argument
• In the data declaration for ‘T’
-}
但是,
instance Functor T where
fmap f (K x y) = absurd (y x)
可以说是一个法律实例。
有人可能会说,利用底部,可以找到上述实例的函子定律的反例。但是,在这种情况下,我想知道所有“标准”函子实例是否实际上是合法的,即使存在底部也是如此。 (也许是吗?)
答案 3 :(得分:4)
有点作弊,但是我们开始吧。根据{{3}},例如,当类型受到约束时,函子不能自动导出。
data A a where
A1 :: (Ord a) => a -> A a
deriving instance Functor A -- doesn't work
实际上,如果(比如说)我们编写了手动版本,那么它也不起作用。
instance Functor A where
fmap f (A1 a) = A1 (f a) -- Can't deduce Ord for f a
但是,由于算法的所有工作都是检查是否存在约束,因此我们可以引入一个类型类,每个类型都是其成员。
class C c
instance C c
现在使用C
而不是Ord
进行上述操作
data B b where
B1 :: (C b) => b -> B b
deriving instance Functor B -- doesn't work
instance Functor B where
fmap f (B1 b) = B1 (f b) -- does work!
答案 4 :(得分:1)
base
中有一个标准类型,称为Compose
,定义如下:
newtype Compose f g a = Compose { getCompose :: f (g a) }
派生的Functor
实例是通过以下方式实现的:
instance (Functor f, Functor g) => Functor (Compose f g) where
fmap f (Compose v) = Compose (fmap (fmap f) v)
但是还有另一个行为完全不同的合法实例:
instance (Contravariant f, Contravariant g) => Functor (Compose f g) where
fmap f (Compose v) = Compose (contramap (contramap f) v)
对我来说,Compose
有两个不同的实例这一事实向我表明,不能自动应用一组规则来覆盖所有可能的情况。