Haskell 我可以有一个多态函数参数,它可能不需要为某些类型提供吗?
我有一个数据类型Haskell 我可以有一个多态函数参数,它可能不需要为某些类型提供吗?,haskell,Haskell,我有一个数据类型F,带有Int的特殊情况: {-# LANGUAGE GADTs, RankNTypes #-} data F a where FGen :: a -> F a FInt :: F Int transformChar :: F Char -> Char transformChar = transform id (error "unreachable code") 在不向调用者公开此数据类型的详细信息的情况下-真正的数据类型更复杂,包含内部实现详细信息-
F
,带有Int
的特殊情况:
{-# LANGUAGE GADTs, RankNTypes #-}
data F a where
FGen :: a -> F a
FInt :: F Int
transformChar :: F Char -> Char
transformChar = transform id (error "unreachable code")
在不向调用者公开此数据类型的详细信息的情况下-真正的数据类型更复杂,包含内部实现详细信息-我想提供一个API来使用它:
transform :: (a -> b) -> b -> F a -> b
transform f i (FGen v) = f v
transform f i FInt = i
如果我要对F Int
调用transform
,显然前两个参数都很重要:
transformInt :: F Int -> Int
transformInt = transform (+1) 5
但是如果我要在F Char
上调用它,第二个参数是不必要的,因为该值不能是FInt
:
{-# LANGUAGE GADTs, RankNTypes #-}
data F a where
FGen :: a -> F a
FInt :: F Int
transformChar :: F Char -> Char
transformChar = transform id (error "unreachable code")
有没有一种方法可以用转换类型来表达这一点
我试过了
transform :: (a -> b) -> (a ~ Int => b) -> F a -> b
transform f i (FGen v) = f v
transform f i FInt = i
但是transformChar
不使用
Couldn't match type ‘Char’ with ‘Int’
Inaccessible code in
a type expected by the context: (Char ~ Int) => Char
In the second argument of ‘transform’, namely
‘(error "unreachable code")’
In the expression: transform id (error "unreachable code")
In an equation for ‘transformChar’:
transformChar = transform id (error "unreachable code")
无论如何,我仍然希望使用荒谬的值,而不是错误来正确地表示编译器应该能够证明代码永远不会被使用。我们可以使用命题等式类型,也可以使用空大小写表达式来表示GHC 7.8中代码的不可访问性:
{-# LANGUAGE GADTs, RankNTypes, EmptyCase, TypeOperators #-}
import Data.Type.Equality
data F a where
FGen :: a -> F a
FInt :: F Int
transform :: (a -> b) -> ((a :~: Int) -> b) -> F a -> b
transform f i (FGen v) = f v
transform f i FInt = i Refl
transformChar :: F Char -> Char
transformChar = transform id (\p -> case p of {})
-- or (\case {}) with LambdaCase
transformInt :: F Int -> Int
transformInt = transform (+1) (const 5)
我喜欢一个更好的答案。这个答案解释了如何对TypeFamilies
执行相同的操作。对于封闭类型族,我们可以将类型中的函数写入类型系统的单位()
和零Void
,以表示介词真与假
{-# LANGUAGE TypeFamilies #-}
import Data.Void
type family IsInt a where
IsInt Int = ()
IsInt a = Void
transform
的第二个参数是()->b
当是a
时,以及Void->b
当a
不是整数时(荒谬的类型)
transform :: (a -> b) -> (IsInt a -> b) -> F a -> b
transform f i (FGen v) = f v
transform f i FInt = i ()
transformChar
可以用的形式编写,而transformInt
必须作为常量函数传入b
transformChar :: F Char -> Char
transformChar = transform id absurd
transformInt :: F Int -> Int
transformInt = transform (+1) (const 5)
更可重复使用
根据建议,我们可以使用类型相等的类型族(==)
,返回提升的Bool
s,使其更易于重用
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE DataKinds #-}
type family (==) a b :: Bool where
(==) a a = True
(==) a b = False
我们可以提供另一个类型系列,将True
转换为()
,将False
转换为Void
。对于这个特定问题,最好从True
或False
和一些类型b
到()->b
或Void->b
type family When p b where
When True b = () -> b
When False b = Void -> b
转换的类型将读取
transform :: (a -> b) -> When (a == Int) b -> F a -> b
你有没有尝试过一个封闭的类型族,它对于每一个非Int
类型都是单位/无效/荒谬的?一般来说,你可以做得更多::t FGen(2::Int);FGen(2::Int)::F Int
GHC对不可满足上下文的处理在这种情况下非常特殊。有人会期望它遵循“ex falso quod libet”。@Cirdec-有趣的想法,我会如何处理类型系列?很好。在这种情况下,我真的希望GHC穷尽性检查器更好:它正确地为空的情况生成警告(好!),但如果我移动到F Int->Int
(坏!),它也会这样做。(这里是GHC7.8——我不知道GHC7.10是否修复了这个问题)谢谢!我所看到的关键点是使用:~:
GADT将不可能的类上下文转换为不可能的值(库中的上下文并不特殊,但通常它是最合乎逻辑的,如果我愿意,我可以定义自己的上下文)还可以使用空大小写表达式来实现(==)
类型族(返回提升的Bool
-s)和IsTrue b
类型族(将提升的Bool转换为()
或Void
),这将更易于重用。