我正在努力在Haskell中重新创建一些基本的类别理论,而且我遇到了一个问题。我的代码看起来像这样 - 非常标准。当然,这是隐藏id和(。)。
class Category c where
id :: forall a. c a a
(.) :: c b d -> c a b -> c a d
-- Recover usual behavior of . and id
instance Category (->) where
id = \x -> x
(.) = \f g x -> f $ g x
class (Category c, Category d) => Functor c d f where
fmap :: c a b -> d (f a) (f b)
data Comp f g x = Comp {getComp :: f (g x)}
instance (Category c, Category d, Category e,
Functor d e f, Functor c d g) => Functor c e (Comp f g) where
fmap = _
最后一个实例声明引发了一个非常难看的错误,
• Could not deduce (Functor d0 e f)
from the context: (Category c, Category d, Category e,
Functor d e f, Functor c d g)
bound by an instance declaration: ...
type variable d0 is ambiguous.
我已经玩了一段时间,我无法理解正在发生的事情。我对haskell很新。
答案 0 :(得分:4)
让我们尝试修复您的代码
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE UndecidableInstances #-}
import Prelude hiding (Functor, fmap, id, (.))
import Data.Coerce
class Category c where
id :: forall a. c a a
(.) :: c b d -> c a b -> c a d
coerceC :: Coercible a b => c a b
-- Recover usual behavior of . and id
instance Category (->) where
id = \x -> x
(.) = \f g x -> f $ g x
coerceC = coerce
class (Category c, Category d) => Functor c d f where
fmap :: c a b -> d (f a) (f b)
到目前为止一切顺利。我向coerceC
添加了Category
方法,我们很快就会看到原因。
(profunctors
包定义.#
和#.
用于相同目的:http://hackage.haskell.org/package/profunctors-5.2.2/docs/Data-Profunctor-Unsafe.html#t:Profunctor)
接下来,我们需要写一些具体的内容来获得洞察力。让我们试试[]
仿函数:
instance Functor (->) (->) [] where
fmap = map
请注意,[]
是一个Functor (->) -> (->)
。洞察力是仿函数
确定/"知道"来源和目的地类别。我们可以告诉GHC
通过FunctionalDependencies
。
class (Category c, Category d) => Functor c d f | f -> c d where
fmap :: c a b -> d (f a) (f b)
然后我们可以继续Comp
示例。注意:
- newtype
工作coerceC
- 我放弃了Category
约束,因为Functor
newtype Comp f g x = Comp {getComp :: f (g x)}
instance (Functor d e f, Functor c d g) => Functor c e (Comp f g) where
fmap = _
现在我们遇到了类型 - 错误,我们期待:
Found hole: _ :: c a b -> e (Comp f g a) (Comp f g b)
我们可以填写
instance (Functor d e f, Functor c d g) => Functor c e (Comp f g) where
fmap :: forall a b. c a b -> e (Comp f g a) (Comp f g b)
fmap ab
= coerceC
. (fmap (fmap ab :: d (g a) (g b)) :: e (f (g a)) (f (g b)))
. coerceC
(写入类型中间签名有助于使事情正确,在这种情况下我们至少需要一个,因为coerceC
是非常多态的)
答案 1 :(得分:1)
这是一个利用功能依赖,安全强制和GADT的解决方案。
我们从原始代码开始,主要是。
{-# LANGUAGE FlexibleInstances, ScopedTypeVariables,
MultiParamTypeClasses, UndecidableInstances,
FunctionalDependencies, GADTs #-}
module Categories where
import Prelude hiding ((.), id, Functor, fmap)
import Data.Coerce
class Category c where
id :: forall a. c a a
(.) :: c b d -> c a b -> c a d
instance Category (->) where
id = \x -> x
(.) = \f g x -> f $ g x
这里我们介绍一个函数依赖项,声明如果我们有一个仿函数f
,这只能在两个类别c, d
之间。如果我们不需要这样做,则f
可以是c, d
之间和c', d'
之间的仿函数,这可以防止GHC推断出"中间类别"当我们在Comp f g
中编写仿函数时。实际上,后者可以是c, e
通过d
之间的仿函数,以及c, e
通过d'
之间的仿函数 - 阻止d
的推断。
class (Category c, Category d) => Functor c d f | f -> d c where
fmap :: c a b -> d (f a) (f b)
然后,我们将Comp
变为newtype
,以便安全强制行动
newtype Comp f g x = Comp {getComp :: f (g x)}
现在是棘手的部分:我们添加了一个类RepresentationalCategory c
,它承认c
具有代表性。也就是说,只要我们可以c a b
强制c a' b'
a
和a'
强制b
,就可以在b'
和c (f (g a)) (f (g b))
之间进行安全强制。
这将用于安全地强制c (Comp f g a) (Comp f g b)
加入c
。在不知道data CoercibleS a b where
Coerc :: Coercible a b => CoercibleS a b
class Category c => RepresentationalCategory c where
representational :: (Coercible a a', Coercible b b') =>
CoercibleS (c a b) (c a' b')
具有代表性的情况下,这两种类型可能完全不同!
(->)
例如,instance RepresentationalCategory (->) where
representational = Coerc
具有代表性。
instance (Category c, Category d, RepresentationalCategory e,
Functor d e f, Functor c d g) => Functor c e (Comp f g) where
fmap (h :: c a b) =
case representational :: CoercibleS (e (f (g a)) (f (g b)))
(e (Comp f g a) (Comp f g b)) of
Coerc -> coerce (fmap (fmap h :: d (g a) (g b)) :: e (f (g a)) (f (g b)))
最后,我们可以编写想要的实例
representational
上面的$conn = new PDO("mysql:host=$servername;dbname=$dbname", $username, $password);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$stmt = $conn->prepare("SELECT * FROM pokemons_en WHERE id = ?");
$stmt->execute(array($q));
证据用于触发正确的强制。我不得不添加一些注释,这些注释会使事情变得有点丑陋,但我认为它仍然具有可读性,并且有一些适度的努力。
答案 2 :(得分:0)
如果将Functor
视为值而不是类型类的实例,则可以执行此操作。
Functor
是两个类别之间的一些映射,它们都使用某种类型函数f
映射对象,并使用fmap
映射态射。
{-# LANGUAGE TypeFamilies, PolyKinds, RankNTypes #-}
class TypeFunction f where
type Apply f x :: k
data Functor c d f = Functor {fmap :: forall a b. c a b -> d (Apply f a) (Apply f b)}
有一些身份类型功能。
data Identity
instance TypeFunction Identity where
type Apply Identity x = x
类型函数可以组合在一起。
{-# LANGUAGE UndecidableInstances #-}
data Comp f g
instance (TypeFunction f, TypeFunction g) => TypeFunction (Comp f g) where
type Apply (Comp f g) x = Apply f (Apply g x)
Functor
可以合成。
compose :: Functor c d g -> Functor d e f -> Functor c e (Comp f g)
compose g f = Functor (\a -> fmap f . fmap g $ a)