打印教堂布尔值
Printing Church Booleans
以下代码旨在将布尔值的 Church 编码打印为 Haskell 的 Bool
:
{-#LANGUAGE FlexibleInstances #-}
instance Show (t -> t -> t) where
show b = show $ b True False
导致此错误的原因:
<interactive>:4:21: error:
• Couldn't match expected type ‘t’ with actual type ‘Bool’
‘t’ is a rigid type variable bound by
the instance declaration at <interactive>:3:10-27
• In the first argument of ‘b’, namely ‘True’
In the second argument of ‘($)’, namely ‘b True False’
In the expression: show $ b True False
• Relevant bindings include
b :: t -> t -> t (bound at <interactive>:4:8)
show :: (t -> t -> t) -> String (bound at <interactive>:4:3)
如何让它发挥作用?
问题是 show :: (t -> t -> t) -> String
应该适用于 any 函数,适用于 any 类型 t
。您假设 t
是布尔值,这是非法的,因为(根据 GHC)“t
是刚性类型变量”并且不能与专用类型统一。
一种可能的解决方案是通过 Bool
(FlexibleInstances
是必要的)
来专门化您的实例
{-#LANGUAGE FlexibleInstances #-}
instance Show (Bool -> Bool -> Bool) where
show b = show $ b True False
但这会降低您教会布尔值的通用性。
不可能定义适用于任何类型的灵活解决方案,因为您将需要有两个代表该类型的描述 true 和 false 情况,并且有像 Void
这样没有(定义的)值的类型。
我想到的一个非常普遍的想法是向 t
添加更多 class 约束:
{-#LANGUAGE FlexibleInstances #-}
import Data.Boolean
instance (Show t, Boolean t) => Show (t -> t -> t) where
show b = show $ b true false
Boolean
class 收集在某些术语中可以理解为逻辑值的类型。例如 Bool
:
instance Boolean Bool where
true = True
false = False
notB = not
(||*) = (||)
(&&*) = (&&)
现在我们可以确保
t
是你实际上可以 show
- 至少有两个有效且不同的类型
t
的值显示为 true
和 false
实际能够以这种方式 show
这种签名的功能需要哪些条件。
重要
以下示例将不起作用:
show (true :: (Show t, Boolean t) => t -> t -> t)
问题是 typechecker 不会猜测您要在此处使用哪个 t
。该解决方案提供了有效且有效的实例,但仅适用于完全实例化的类型。如果出现歧义错误,则需要指定 t
:
show (true :: Bool -> Bool -> Bool)
>>> "True"
show (true :: Int -> Int -> Int) -- assuming Boolean instance
>>> "1"
编辑:
评论中还提到了另一个想法。解决方案是用 Rank2Type
:
包装你的 Church 布尔值
{-# LANGUAGE Rank2Types #-}
newtype ChBool = ChBool (forall t. t -> t -> t)
这将使 t
成为独立于上下文的任何类型。然后你可以像这样定义临时实例:
instance Show ChBool where
show (ChBool f) = show $ f True False
以下代码旨在将布尔值的 Church 编码打印为 Haskell 的 Bool
:
{-#LANGUAGE FlexibleInstances #-}
instance Show (t -> t -> t) where
show b = show $ b True False
导致此错误的原因:
<interactive>:4:21: error:
• Couldn't match expected type ‘t’ with actual type ‘Bool’
‘t’ is a rigid type variable bound by
the instance declaration at <interactive>:3:10-27
• In the first argument of ‘b’, namely ‘True’
In the second argument of ‘($)’, namely ‘b True False’
In the expression: show $ b True False
• Relevant bindings include
b :: t -> t -> t (bound at <interactive>:4:8)
show :: (t -> t -> t) -> String (bound at <interactive>:4:3)
如何让它发挥作用?
问题是 show :: (t -> t -> t) -> String
应该适用于 any 函数,适用于 any 类型 t
。您假设 t
是布尔值,这是非法的,因为(根据 GHC)“t
是刚性类型变量”并且不能与专用类型统一。
一种可能的解决方案是通过 Bool
(FlexibleInstances
是必要的)
{-#LANGUAGE FlexibleInstances #-}
instance Show (Bool -> Bool -> Bool) where
show b = show $ b True False
但这会降低您教会布尔值的通用性。
不可能定义适用于任何类型的灵活解决方案,因为您将需要有两个代表该类型的描述 true 和 false 情况,并且有像 Void
这样没有(定义的)值的类型。
我想到的一个非常普遍的想法是向 t
添加更多 class 约束:
{-#LANGUAGE FlexibleInstances #-}
import Data.Boolean
instance (Show t, Boolean t) => Show (t -> t -> t) where
show b = show $ b true false
Boolean
class 收集在某些术语中可以理解为逻辑值的类型。例如 Bool
:
instance Boolean Bool where
true = True
false = False
notB = not
(||*) = (||)
(&&*) = (&&)
现在我们可以确保
t
是你实际上可以show
- 至少有两个有效且不同的类型
t
的值显示为true
和false
实际能够以这种方式 show
这种签名的功能需要哪些条件。
重要
以下示例将不起作用:
show (true :: (Show t, Boolean t) => t -> t -> t)
问题是 typechecker 不会猜测您要在此处使用哪个 t
。该解决方案提供了有效且有效的实例,但仅适用于完全实例化的类型。如果出现歧义错误,则需要指定 t
:
show (true :: Bool -> Bool -> Bool)
>>> "True"
show (true :: Int -> Int -> Int) -- assuming Boolean instance
>>> "1"
编辑:
评论中还提到了另一个想法。解决方案是用 Rank2Type
:
{-# LANGUAGE Rank2Types #-}
newtype ChBool = ChBool (forall t. t -> t -> t)
这将使 t
成为独立于上下文的任何类型。然后你可以像这样定义临时实例:
instance Show ChBool where
show (ChBool f) = show $ f True False