理解Haskell';s Cast运算符
假设我们已经导入了理解Haskell';s Cast运算符,haskell,Haskell,假设我们已经导入了Data.Typeable,其中包含cast::(Typeable a,Typeable b)->a->Maybe b 考虑一下 > cast 'n' :: Maybe Char Just 'n' 然而 我理解上述内容,但这似乎只是一个微不足道的例子。它没有揭示为什么有人需要使用cast操作符(据我所知) 问题:是否有一个使用cast的示例,它真正地将值的类型从一种类型“更改”为另一种类型?我能想到的最接近的例子(实际上在GHCi中不起作用)是将7的类型从Integer
Data.Typeable
,其中包含cast::(Typeable a,Typeable b)->a->Maybe b
考虑一下
> cast 'n' :: Maybe Char
Just 'n'
然而
我理解上述内容,但这似乎只是一个微不足道的例子。它没有揭示为什么有人需要使用cast
操作符(据我所知)
问题:是否有一个使用cast
的示例,它真正地将值的类型从一种类型“更改”为另一种类型?我能想到的最接近的例子(实际上在GHCi中不起作用)是将7
的类型从Integer
更改为Double
:
> cast 7 :: Maybe Double
Just '7' -- this doesn't work, as 7 is typed as a Integer above; instead GHCi returns Nothing
是否有使用cast真正“改变”值类型的例子
简单的答案是否定的。但是,可能有一种情况,你不知道你的类型,因为它被量词“存在”所“隐藏”。下面是一个例子:
{-# LANGUAGE ExistentialQuantification #-}
import Data.Typeable
import Data.Maybe
data Foo = forall a . Typeable a => Foo a
main = print . catMaybes . map (\(Foo x) -> cast x :: Maybe Bool) $ xs
where
xs = [Foo True, Foo 'a', Foo "str", Foo False]
输出将是:
[True,False]
Typeable
类的目的是允许您在不知道数据的确切类型时处理数据。cast
运算符的目的是检查某些数据是否具有特定类型,如果是,则允许您将其作为该类型使用。这一切都是关于对数据的类型签名进行处理。实际值根本不会改变
如果要更改某些数据的值,这不是强制转换,而是转换。各种各样的功能都可以做到这一点。例如,
from Integral
将作为Integral
实例的某个内容转换为其他内容。(例如,Int
到Double
)这不是强制转换的目的。假设您想要实现一个函数,该函数在所有值上充当标识函数,除了整数,它应该充当后续函数
在Python等非类型化语言中,这很简单:
>>定义f(x):
... 如果类型(x)==int:
... 返回x+1
... 其他:
... 返回x
...
>>>f(42)
43
>>>f(“qwerty”)
“qwerty”
即使在Java中,这也是可行的,即使它需要一些强制转换:
与该类型关联的静态
但是,如果我们可以在类型a
上添加Typeable
约束,则可以:
f :: forall a. Typeable a => a -> a
f x = case cast x :: Maybe Int of
Nothing -> x
Just n -> case cast (n+1) :: Maybe a of
Nothing -> x
Just y -> y
第一个cast
将a
转换为Int
,第二个cast
将a
转换回Int
上面的代码非常难看,因为我们知道第二次强制转换不能失败,所以不需要第二次强制转换。我们可以使用eqT
和类型相等GADT改进上述代码:
f :: forall a. Typeable a => a -> a
f x = case eqT :: Maybe (a :~: Int) of
Nothing -> x
Just Refl -> x+1
基本上,eqT
告诉我们两种(Typeable
)类型是否相等。更好的是,在与Just Refl
匹配后,编译器还知道它们是相等的,并允许我们使用Int
值而不是a
值,可以互换,并且不使用强制转换
事实上,多亏了GADT、:~:
和eqT
,现在大部分的cast
的使用都已经过时了
过时的<代码>强制转换本身可以通过eqT
轻松实现:
cast :: forall a. (Typeable a, Typeable b) => a -> Maybe b
cast x = case eqT :: Maybe (a :~: b) of
Nothing -> Nothing
Just Refl -> Just x
结论:在哈斯克尔,我们得到了两个世界的最好。对于多态类型,我们确实有参数保证(自由定理)。我们还可以打破这些参数保证,使用“特殊”多态性,代价是额外的可键入的约束。您可能感兴趣。它早于Data.concure.concure
,因此今天有些事情可能会有所不同。
cast :: forall a. (Typeable a, Typeable b) => a -> Maybe b
cast x = case eqT :: Maybe (a :~: b) of
Nothing -> Nothing
Just Refl -> Just x