2016-08-16 101 views
1

比方说,我们有以下几点:约束情况下

{-# LANGUAGE TypeFamilies #-} 
{-# LANGUAGE ConstraintKinds #-} 
{-# LANGUAGE FlexibleInstances #-} 
{-# LANGUAGE TypeFamilyDependencies #-} 

type family CategoryLikeT p_a_b = t | t -> p_a_b 

type IsCategoryLike p t a b = (t ~ CategoryLikeT (p, a, b)) 

class CategoryLike p where 
    (>>>) :: 
    (
     IsCategoryLike p t1 a b, 
     IsCategoryLike p t2 b c, 
     IsCategoryLike p t3 a c 
    ) => t1 -> t2 -> t3 

然后,我们发现,这个编译罚款:

f :: 
    (
    CategoryLike p, 
    IsCategoryLike p t1 a b, 
    IsCategoryLike p t2 b c, 
    IsCategoryLike p t3 c d, 
    IsCategoryLike p t4 a d 
) => t1 -> t2 -> t3 -> t4 
f x y z = x >>> y >>> z 

但是,我们还没有定义任何实例呢。让我们做到这一点:

data BasicFunction 
type instance CategoryLikeT (BasicFunction, a, b) = a -> b 

instance CategoryLike BasicFunction where 
    (>>>) = flip (.) 

而且“INTS”下,除了是一种类像,如果我们只是假设“A”和“B”都是Void,例如: 数据BasicInt 类型实例CategoryLikeT( BasicInt,虚空,虚空)= INT

instance CategoryLike BasicFunction where 
    (>>>) = (+) 

当然上述不起作用,因为在实例定义有关于“A”或“b”没有约束,所以没有机制保障>>>得到都是一样的类型,因此(+)不够一般。因此,我认为是做了以下内容:

首先,增加了约束类型:

type family CategoryConstraints p t a b 

,然后加入到IsCategoryLike像下面这样的定义:

type IsCategoryLike p t a b = 
    (t ~ CategoryLikeT (p, a, b), CategoryConstraints p t) 

我们可以再加入以下约束:

type instance CategoryConstraints BasicInt t = (t ~ Int) 

但是现在我们有一个问题。 f不再起作用,给这个错误:

Could not deduce: CategoryConstraints p (CategoryLikeT (p, a, c))) 

我们可以解决这个问题有两种方式:

首先,通过在f加入IsCategoryLike p t5 a c的制约。但是对于更复杂的函数,这可能会很快变得非常混乱,您必须为每个操作添加一个约束。同样微不足道的变化,如将(x >>> y) >>> z更改为x >>> (y >>> z)需要更改签名,当没有约束时不需要签名。

或者,可以完全省略类型签名,或者可以使用部分类型签名。

但是,我想保留完整类型的签名,但不增长并且难以维护。人们可以提出替代方法吗?

回答

3

嗯...我不确定这是最好的方法,但这是对你有什么的直接改进。特别是,我认为使用关联类型会使事情变得更清洁。

{-# LANGUAGE TypeFamilies, 
      ConstraintKinds, 
      FlexibleInstances, 
      TypeFamilyDependencies #-} 

import GHC.Exts (Constraint) 

class CategoryLike p where 
    type CategoryLikeT p a b = t | t -> p a b 
    type CategoryConstraints p a b :: Constraint 
    type CategoryConstraints p a b =() 
    (>>>) :: (CategoryConstraints p a b, CategoryConstraints p b c, CategoryConstraints p a c) 
    => CategoryLikeT p a b -> CategoryLikeT p b c -> CategoryLikeT p a c 

data BasicFunction 
instance CategoryLike BasicFunction where 
    type CategoryLikeT BasicFunction a b = a -> b 
    (>>>) = flip (.) 

data BasicInt 
instance CategoryLike BasicInt where 
    type CategoryLikeT BasicInt Int Int = Int 
    type CategoryConstraints BasicInt a b = (a ~ Int, b ~ Int) 
    (>>>) = (+) 

所以,这是f貌似现在(我有一个明确的FORALL写它,因为它使一个候选人使用TypeApplications

f :: forall p a b c d. (
    CategoryLike p, 
    CategoryConstraints p a b, 
    CategoryConstraints p b c, 
    CategoryConstraints p a c, 
    CategoryConstraints p a d, 
    CategoryConstraints p d b 
) => CategoryLikeT p a d -> 
     CategoryLikeT p d b -> 
     CategoryLikeT p b c -> 
     CategoryLikeT p a c 
f x y z = x >>> y >>> z 

要使用它,我可以做这样的事情(这看起来令人惊讶):

ghci> :set -XTypeApplications 
ghci> :t f @BasicFunction (+1) id show 
f @BasicFunction (+1) id show :: (Show a, Num a) => a -> [Char] 
ghci> :t f @BasicInt 1 2 3 
f @BasicInt 1 2 3 :: Int 
+0

关键问题是,你可以用类型签名写'f'吗? – Clinton

+0

@Clinton仍然不是一个很好的签名,但更好一点。至少它可以与可见类型的应用程序一起使用 – Alec

相关问题