Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/8.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 - Fatal编程技术网

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
),这将更易于重用。