无法从上下文错误中推断出包含的约束

时间:2018-05-30 18:25:46

标签: haskell category-theory

我正在努力在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很新。

3 个答案:

答案 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' aa'强制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)