3

我不明白first库中的实现。

first似乎是用递归定义的***——我看不到递归何时结束!?

first :: a b c -> a (b,d) (c,d)
first = (*** id)

(***) :: a b c -> a b' c' -> a (b,b') (c,c')
f *** g = first f >>> arr swap >>> first g >>> arr swap
    where swap ~(x,y) = (y,x)

first f(f *** id)哪个是(first f >>> arr swap >>> first id...),新的first将是另一个(*** id),依此类推...

4

1 回答 1

3

你是对的,如果你实现这样的箭头:

instance Arrow MyType where
    arr f = ...

然后尝试使用firstor (***),你会得到一个无限循环,因为实现相互引用是非生产性的。但是,以这种方式定义默认方法允许您实例Arrow化为

instance Arrow MyType where
    arr f = ...
    first t = ...

或者

instance Arrow MyType where
    arr f = ...
    t *** t' = ...

哪个更方便/更有效(取决于您关心的内容),并且缺少的方法将根据您指定的方法自动定义。

如果我们尝试实例化Arrow而不给出firstor的实现(***),我们将收到以下警告:

warning: [-Wmissing-methods]
• No explicit implementation for
    either ‘first’ or ‘***’
• In the instance declaration for ‘Arrow MyType’

这是因为带有一个MINIMALpragma

{-# MINIMAL arr, (first | (***)) #-}

它告诉编译器,即使提供了默认值,实例也不完整,除非它指定arrfirstor之一(***)。所以需求被编码和检查。


至于你的评论:

不一定我可以保留默认值,然后递归将根据定义进行。具体实现不是这里的问题......

没有实例就不能使用类型类的方法。甚至很少可以尝试,因为方法的类型总是引用类型,例如

class Arrow k where
    first :: k a b -> k (a,c) (b,c)
    ...

当您使用 时first,您必须有一个特定k的想法才能使用结果,例如

print $ first (arr id) (1,2)                -- using it at k ~ (->)
print =<< runKleisli (first (arr id)) (1,2) -- using it at Kleisli IO

在某些时候,程序的类型约束将k确定为具体的东西,这就是使用的实例。你不能使用没有实例的类。

(即使在事情以无法确定特定实例的方式排列的情况下,经典的例子是

show . read :: String -> String

编译器只会对你大喊大叫

• Ambiguous type variable ‘a0’ arising from a use of ‘read’
   prevents the constraint ‘(Read a0)’ from being solved.
   Probable fix: use a type annotation to specify what ‘a0’ should be.

如果程序无法编译,则没有无限递归!)

于 2019-09-02T03:11:59.687 回答