我正在阅读ClassyPrelude的公告,并且到了这里:
instance (b ~ c, CanFilterFunc b a) => CanFilter (b -> c) a where
filter = filterFunc
作者随后提到这不起作用:
instance (CanFilterFunc b a) => CanFilter (c -> c) a where
filter = filterFunc
这对我有意义,因为c
与左边的约束完全无关。
然而,文章中没有提及的内容以及我不理解的是为什么这不起作用:
instance (CanFilterFunc b a) => CanFilter (b -> b) a where
filter = filterFunc
有人可以解释为什么这与第一个提到的定义不同?也许GHC类型推理的一个有效例子会有帮助吗?
答案 0 :(得分:52)
我们需要以下扩展名:
{-# LANGUAGE FlexibleInstances, TypeFamilies #-}
让我们定义一个比CanFilter
更简单的类,只有一个参数。我正在定义该类的两个副本,因为我想证明两个实例之间的行为差异:
class Twice1 f where
twice1 :: f -> f
class Twice2 f where
twice2 :: f -> f
现在,让我们为每个类定义一个实例。对于Twice1
,我们将类型变量直接修改为相同,对于Twice2
,我们允许它们不同,但添加一个等式约束。
instance Twice1 (a -> a) where
twice1 f = f . f
instance (a ~ b) => Twice2 (a -> b) where
twice2 f = f . f
为了显示差异,让我们定义另一个重载函数,如下所示:
class Example a where
transform :: Int -> a
instance Example Int where
transform n = n + 1
instance Example Char where
transform _ = 'x'
现在我们处于可以看到差异的地步。一旦我们定义了
apply1 x = twice1 transform x
apply2 x = twice2 transform x
并向GHC询问推断类型,我们得到了
apply1 :: (Example a, Twice1 (Int -> a)) => Int -> a
apply2 :: Int -> Int
为什么?好吧,Twice1
的实例仅在函数的源类型和目标类型相同时触发。对于transform
和给定的上下文,我们不知道。 GHC将仅在右侧匹配时应用实例,因此我们留下未解决的上下文。如果我们试图说apply1 0
,则会出现类型错误,说明仍然没有足够的信息来解决重载问题。在这种情况下,我们必须明确指定结果类型为Int
才能通过。
但是,在Twice2
中,实例适用于任何函数类型。 GHC将立即解决它(GHC永远不会回溯,所以如果一个实例明显匹配,它总是被选中),然后尝试建立前提条件:在这种情况下,相等约束,然后强制结果类型为{{1}并允许我们解决Int
约束。我们可以在没有其他类型注释的情况下说Example
。
所以这是关于GHC实例解析的一个相当微妙的观点,这里的等式约束有助于GHC的类型检查器以一种需要用户更少类型注释的方式。
答案 1 :(得分:0)
完成@kosmikus答案
对purescript同样适用-您需要相等约束才能正确导出类型(您可以在此处http://try.purescript.org尝试)
module Main where
import Prelude
-- copied from https://github.com/purescript/purescript-type-equality/blob/master/src/Type/Equality.purs
class TypeEquals a b | a -> b, b -> a where
to :: a -> b
from :: b -> a
instance refl :: TypeEquals a a where
to a = a
from a = a
-----------------
class Twice1 f where
twice1 :: f -> f
class Twice2 f where
twice2 :: f -> f
instance mytwice1 :: Twice1 (a -> a) where
twice1 f = f >>> f
instance mytwice2 :: TypeEquals a b => Twice2 (a -> b) where
twice2 f = f >>> from >>> f
class Example a where
transform :: Int -> a
instance exampleInt :: Example Int where
transform n = n + 1
instance exampleChar :: Example Char where
transform _ = 'x'
{--
-- will raise error
-- No type class instance was found for Main.Twice1 (Int -> t1)
apply1 x = twice1 transform x
-- to resolve error add type declaration
apply1 :: Int -> Int
--}
-- compiles without error and manual type declaration, has type Int -> Int automatically
apply2 x = twice2 transform x
但是在idris中,你不会
module Main
import Prelude
interface Twice f where
twice : f -> f
Twice (a -> a) where
twice f = f . f
interface Example a where
transform : Int -> a
Example Int where
transform n = n + 1
Example Char where
transform _ = 'x'
-- run in REPL to see that it derives properly:
-- $ idris src/15_EqualityConstraint_Twice_class.idr
-- *src/15_EqualityConstraint_Twice_class> :t twice transform
-- twice transform : Int -> Int
-- Summary:
-- in idris you dont need equality constaint to derive type of such functions properly