Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/magento/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell 箭头定律:第一个仅取决于对的第一个分量。我们为什么需要这个?_Haskell_Monads_Arrows_Combinators_Kleisli - Fatal编程技术网

Haskell 箭头定律:第一个仅取决于对的第一个分量。我们为什么需要这个?

Haskell 箭头定律:第一个仅取决于对的第一个分量。我们为什么需要这个?,haskell,monads,arrows,combinators,kleisli,Haskell,Monads,Arrows,Combinators,Kleisli,约翰·休斯(John Hughes)在其《将单子概括为箭头》(第8章)中写道: 我们将第一个f仅依赖于对的第一个分量的属性形式化,如下所示: first f>>>arr fst=arr fst>>>f 我知道法律过滤掉了这类实施: newtype KleisliMaybe a b = KMb { runKMb :: a -> Maybe b } instance Category KleisliMaybe where ... instance Arrow KleisliMaybe

约翰·休斯(John Hughes)在其《将单子概括为箭头》(第8章)中写道:

我们将
第一个f
仅依赖于对的第一个分量的属性形式化,如下所示:
first f>>>arr fst=arr fst>>>f

我知道法律过滤掉了这类实施:

newtype KleisliMaybe a b = KMb { runKMb :: a -> Maybe b }

instance Category KleisliMaybe where 
 ...

instance Arrow KleisliMaybe where
 first f = KMb $ const Nothing
 ...
但在这种情况下,措辞似乎有点奇怪(我会选择“
首先
没有副作用”或类似的例子)

那么,还有什么其他理由让它继续存在呢

此外,还有另一条定律:
first f>>>second(arrg)=second(arrg)>>>first f
。我没有找到它过滤掉的任何实现(我找到了-请参阅编辑)。这项法律对我们有什么帮助


编辑:关于后一条法则的更多想法。

请看以下代码段:

newtype KleisliWriter = KW { runKW :: a -> (String, b) }

instance Category KleisliWriter where
 ...

instance Arrow KleisliWriter where
 arr f = KW $ \ x -> ("", f x)
 first  (KW f) = KW $ \ ~(a, d) -> f a >>= (\ x -> ("A", (x, d)))
 second (KW f) = KW $ \ ~(d, b) -> f b >>= (\ x -> ("B", (d, x)))
此类实例的行为方式如下:

GHCi> c = KW $ \ x -> ("C", x)
GHCi> fst . runKW (first c >>> second (arr id)) $ (1, 2)
"CAB"
GHCi> fst . runKW (second (arr id) >>> first c) $ (1, 2)
"BCA"
就我所知,我们没有关于第二个f=swap>>第一个f>>swap的法律。因此,我们可以禁止
second
first
对该法律产生任何副作用。然而,最初的措辞似乎仍然没有再次暗示:

…我们将成对的第二个分量不受第一个f影响的直觉形式化为一条定律


这些定律仅仅是可靠证明的形式化吗?

简短回答:有一对不同的定律,涵盖了“
第一个和
第二个
没有副作用”:

思考之后,我认为你已经确定的两条定律:

first f >>> arr fst          =   arr fst >>> f                -- LAW-A
first f >>> second (arr g)   =   second (arr g) >>> first f   -- LAW-B
事实上,它们是多余的,因为它们遵循那些没有副作用的定律、其他定律和一些“自由定理”

你的反例违反了无副作用法,所以这就是为什么它们也违反了A法和/或B法。如果有人有一个真正的反例,它遵守了无副作用法,但违反了A法或B法,我会非常有兴趣看到它

长答案:

财产“
首先
没有副作用(至少它本身)”最好由该条第8节前面所述的法律形式化:

first (arr f) = arr (first f)
回想一下,休斯曾说过,如果一支箭可以写成
arr expr
,它就是“纯的”(相当于“没有副作用”)。因此,该定律规定,如果任何计算已经是纯的,因此可以写入
arrf
,那么将
first
应用于该计算也会导致纯计算(因为它的形式是
arrfpr
expr=first f
)。因此,
首先
不会引入杂质/自身不会产生影响

其他两条法律:

first f >>> arr fst          =   arr fst >>> f                -- LAW-A
first f >>> second (arr g)   =   second (arr g) >>> first f   -- LAW-B
旨在捕捉以下思想:对于特定的
实例Arrow Foo
和特定的Arrow action
f::Foo B C
,操作:

first f :: forall d. Foo (B,d) (C,d)
arr (\(x,y) -> (x, g y)) >>> (first f >>> arr fst) = first f >>> arr fst
作用于其输入/输出对的第一个组件,就像第二个组件不存在一样。法律对应于以下属性:

  • 法则A:输出成分
    C
    和任何副作用仅取决于输入
    B
    ,而非输入
    d
    (即,不依赖于
    d
  • 定律B:组件
    d
    未受输入
    B
    或任何副作用的影响(即对
    d
    无影响),未发生变化
  • 关于法-A,如果考虑动作<代码> F::FoO(b,d)(c,d)< /代码>,并使用纯函数将其输出的代码C>/C>组件集中到:

    first f >>> arr fst :: Foo (B,d) C
    
    然后,结果与我们首先使用纯动作强制移除第二个组件的结果相同:

    arr fst :: Foo (B,d) B
    
    并允许原始操作
    f
    仅作用于
    B

    arr fst >>> f :: Foo (B,d) C
    
    这里,
    first f>>>arr fst
    动作的结构使得
    first f
    可能依赖于输入的
    d
    分量来制定其副作用并构建其输出的
    C
    分量;但是,
    arr fst>>f
    操作的结构通过在允许通过
    f
    进行任何非平凡计算之前通过纯操作移除
    d
    组件,消除了这种可能性。这两个动作相等的事实(定律)清楚地表明,
    first f
    B
    输入中产生
    C
    输出(以及通过
    f
    产生的副作用,因为
    first
    本身没有额外的效果),其方式也不能依赖于
    d
    输入

    B定律更难。将该属性形式化的最明显方式是伪法律:

    first f >>> arr snd = arr snd
    
    这直接表明
    第一个f
    不会改变提取的(
    arr snd
    )第二个分量。然而,休斯指出,这是限制性的,因为它不允许
    第一个f
    产生副作用(或至少任何可以在纯动作
    arr snd
    中存活的副作用)。相反,他提供了更复杂的法律:

    first f >>> second (arr g)   =   second (arr g) >>> first f
    
    这里的想法是,如果
    first f
    修改了
    d
    值,那么在某些情况下,以下两个操作将不同:

    -- `first f` changes `inval` to something else
    second (arr (const inval)) >>> first f
    -- if we change it back, we change the action
    second (arr (const inval)) >>> first f >>> second (arr (const inval))
    
    但是,由于B法,我们有:

    second (arr (const inval)) >>> first f >>> second (arr (const inval))
    -- associativity
    = second (arr (const inval)) >>> (first f >>> second (arr (const inval)))
    -- LAW-B
    = second (arr (const inval)) >>> (second (arr (const inval)) >>> first f)
    -- associativity
    = (second (arr (const inval)) >>> (second (arr (const inval))) >>> first f
    -- second and arr preserve composition
    = second (arr (const inval >>> const inval)) >>> first f
    -- properties of const function
    = second (arr (const inval)) >>> first f
    
    因此,这些行为是相同的,与我们的假设相反

    然而,我猜想A定律和B定律都是多余的,因为我相信(见下面我的犹豫)它们遵循其他定律加上签名的“自由定理”:

    first f :: forall d. Foo (B,d) (C,d)
    
    假设
    first
    second
    满足无副作用定律:

    first (arr f) = arr (first f)
    second (arr f) = arr (second f)
    
    那么定律B可以改写为:

    first f >>> second (arr g)              = second (arr g) >>> first f
    -- no side effects for "second"
    first f >>> arr (second g)              = arr (second g) >>> first f
    -- definition of "second" for functions
    = first f >>> arr (\(x,y) -> (x, g y))  = arr (\(x,y) -> (x, g y)) >>> first f
    
    最后这句话就是第一个f的自由定理。(直观地说,由于
    first f
    d
    类型中是多态的,因此
    d
    上的任何纯动作都必然对
    first f
    是“不可见的”,因此
    
    
    first f >>> arr fst :: forall d. Foo (B,d) C
    
    arr (\(x,y) -> (x, g y)) >>> (first f >>> arr fst) = first f >>> arr fst
    
    -- by associativity
    (arr (\(x,y) -> (x, g y)) >>> first f) >>> arr fst
    -- by rewritten version of LAW-B
    (first f >>> arr (\(x,y) -> (x, g y))) >>> arr fst
    -- by associativity
    first f >>> (arr (\(x,y) -> (x, g y)) >>> arr fst)
    -- `arr` preserves composition
    first f >>> arr ((\(x,y) -> (x, g y)) >>> fst)
    -- properties of fst
    first f >>> arr fst
    
    > runKMb (first (arr (2*))) (2,3)
    Nothing
    > runKMb (arr (first (2*))) (2,3)
    Just (4,3)
    
    > runKW (first (arr (2*))) (1,2)
    ("A",(2,2))
    > runKW (arr (first (2*))) (1,2)
    ("",(2,2))