假设我们已导入Data.Typeable
,其中包含cast :: (Typeable a, Typeable b) -> a -> Maybe b
。
考虑一下
> cast 'n' :: Maybe Char
Just 'n'
还
> cast 7 :: Maybe Char
Nothing
我理解上述情况,但这似乎是一个微不足道的例子。它没有透露为什么有人需要使用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
答案 0 :(得分:7)
是否有一个使用强制转换的例子,它真正将某种类型的值从一种类型“改变”到另一种类型?
简单的答案是否定的。但是,可能存在一种你不知道你的类型的情况,因为它被量词“存在”“隐藏”了。这是一个例子:
{-# 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]
答案 1 :(得分:5)
Typeable
课程的目的是让您在不了解确切类型时使用数据。 cast
运算符的目的是检查某些数据是否具有特定类型,如果是,则允许您将其作为该类型使用。这完全是为了对数据的类型签名做些什么。实际的值根本不会改变。
如果您想更改某些数据的值,那不是演员,那就是转换。有各种各样的功能可以做到这一点。例如,fromIntegral
将Integral
的实例变为其他内容。 (例如,Int
到Double
。)然而, cast 不是什么。
答案 2 :(得分:4)
假设您想要在所有值上实现一个充当身份函数的函数,除了它应该作为后继函数的整数。
在像Python这样的无类型语言中,这很简单:
>>> def f(x):
... if type(x) == int:
... return x+1
... else:
... return x
...
>>> f(42)
43
>>> f("qwerty")
'qwerty'
即使在Java中,这也是可行的,即使它需要一些演员:
static <A> A f(A a) {
if (a instanceof Integer)
return (A) (Integer) ((Integer) a + 1);
else
return a;
}
public static void main (String[] args) throws java.lang.Exception
{
System.out.println(">> " + f(42));
System.out.println(">> " + f("qwerty"));
}
/* Output:
>> 43
>> qwerty
*/
Haskell怎么样?
在Haskell中,这是不可能完成的。类型forall a. a -> a
的任何函数都必须无法终止,或者表现为身份。这是与该类型相关联的free theorem的直接结果。
但是,如果我们可以在类型Typeable
上添加a
约束,那么就有可能:
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
的大多数用途都是
过时。使用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
结论:在Haskell中,我们得到了两全其美。我们确实有多态类型的参数保证(自由定理)。我们还可以打破这些参数保证,并使用“ad hoc”多态,代价是额外的Typeable
约束。