簡體   English   中英

是否可以在不破壞等式推理的情況下使用教會編碼?

[英]Is it possible to use church encodings without breaking equational reasoning?

記住這個計划:

{-# LANGUAGE RankNTypes #-}

import Prelude hiding (sum)

type List h = forall t . (h -> t -> t) -> t -> t

sum_ :: (Num a) => List a -> a
sum_ = \ list -> list (+) 0

toList :: [a] -> List a
toList = \ list cons nil -> foldr cons nil list

sum :: (Num a) => [a] -> a
-- sum = sum_ . toList        -- does not work
sum = \ a -> sum_ (toList a)  -- works

main = print (sum [1,2,3])

總和的兩個定義都是相同的,直到等式推理。 然而,編譯第二個作品的定義,但第一個定義沒有,這個錯誤:

tmpdel.hs:17:14:
    Couldn't match type ‘(a -> t0 -> t0) -> t0 -> t0’
                  with ‘forall t. (a -> t -> t) -> t -> t’
    Expected type: [a] -> List a
      Actual type: [a] -> (a -> t0 -> t0) -> t0 -> t0
    Relevant bindings include sum :: [a] -> a (bound at tmpdel.hs:17:1)
    In the second argument of ‘(.)’, namely ‘toList’
    In the expression: sum_ . toList

似乎RankNTypes打破了等式推理。 有沒有辦法在Haskell中有教會編碼的列表而不破壞它?

我的印象是ghc盡可能地滲透所有人:

forall a t. [a] -> (a -> t -> t) -> t -> t)

forall a. [a] -> forall t . (h -> t -> t) -> t -> t

可以互換使用,見證者:

toList' :: forall a t. [a] -> (a -> t -> t) -> t -> t
toList' = toList

toList :: [a] -> List a
toList = toList'

這可以解釋為什么sum的類型無法檢查。 您可以通過將多態定義打包在newtype包裝中來避免此類問題,以避免這種提升 (該段落不會出現在較新版本的doc中,因此我之前使用條件)。

{-# LANGUAGE RankNTypes #-}
import Prelude hiding (sum)

newtype List h = List { runList :: forall t . (h -> t -> t) -> t -> t }

sum_ :: (Num a) => List a -> a
sum_ xs = runList xs (+) 0

toList :: [a] -> List a
toList xs = List $ \ c n -> foldr c n xs

sum :: (Num a) => [a] -> a
sum = sum_ . toList

main = print (sum [1,2,3])

這是一個你可以嘗試的有點可怕的技巧。 你將擁有一個rank-2類型的變量,而是使用一個空類型; 在任何地方你都會選擇類型變量的實例化,使用unsafeCoerce 使用空類型可以確保(盡可能多)您不會執行任何可以觀察應該是不可觀察值的內容。 因此:

import Data.Void
import Unsafe.Coerce

type List a = (a -> Void -> Void) -> Void -> Void

toList :: [a] -> List a
toList xs = \cons nil -> foldr cons nil xs

sum_ :: Num a => List a -> a
sum_ xs = unsafeCoerce xs (+) 0

main :: IO ()
main = print (sum_ . toList $ [1,2,3])

你可能想寫一個稍微安全的unsafeCoerce版本,比如:

instantiate :: List a -> (a -> r -> r) -> r -> r
instantiate = unsafeCoerce

然后sum_ xs = instantiate xs (+) 0作為替代定義可以正常工作,並且您不會冒險將List a變為任意的TRULY。

通常,等式推理僅存在於Haskell所代表的“基礎系統F”中。 在這種情況下,正如其他人所指出的,你要絆倒了,因為Haskell的移動forall小號向左,並自動在不同的點采用了適當的類型。 你可以通過提供類型應用程序應該通過newtype包裝器發生的位置來解決它。 正如你所見,你也可以通過eta擴展來操作類型應用程序,因為Hindley-Milner的輸入規則對於let和lambda是不同的: forall是通過“泛化”規則引入的,默認情況下是在let s(和另外,相當於命名綁定)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM