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_Existential Type - Fatal编程技术网

返回存在类型的Haskell函数

返回存在类型的Haskell函数,haskell,existential-type,Haskell,Existential Type,是否可以编写一个Haskell函数,在隐藏确切类型参数的情况下生成参数化类型?例如,类似于f::T->(存在a.U a)?显而易见的尝试是: {-# LANGUAGE ExistentialQuantification #-} data D a = D a data Wrap = forall a. Wrap (D a) unwrap :: Wrap -> D a unwrap (Wrap d) = d 无法使用以下命令编译: Couldn't match type `a1' wi

是否可以编写一个Haskell函数,在隐藏确切类型参数的情况下生成参数化类型?例如,类似于
f::T->(存在a.U a)
?显而易见的尝试是:

{-# LANGUAGE ExistentialQuantification #-}

data D a = D a

data Wrap = forall a. Wrap (D a)

unwrap :: Wrap -> D a
unwrap (Wrap d) = d
无法使用以下命令编译:

Couldn't match type `a1' with `a'
  `a1' is a rigid type variable bound by
       a pattern with constructor
         Wrap :: forall a. D a -> Wrap,
       in an equation for `unwrap'
       at test.hs:8:9
  `a' is a rigid type variable bound by
      the type signature for unwrap :: Wrap -> D a at test.hs:7:11
Expected type: D a
  Actual type: D a1
In the expression: d
In an equation for `unwrap': unwrap (Wrap d) = d
我知道这是一个人为的例子,但我很好奇是否有办法让GHC相信我不关心
D
参数化的确切类型,而不为
unwrap
的结果引入另一种存在包装类型


为了澄清,我确实希望类型安全,但也希望能够将一个函数
dToString::da->String
,它不关心
a
(例如,因为它只是从
D
提取字符串字段)应用于
展开的结果。我意识到还有其他方法可以实现它(例如,定义
wrapToString(Wrap d)=dToString d
),但我更感兴趣的是,是否有一个根本原因导致不允许在存在主义下进行这种隐藏。

是的,您可以说服GHC您不关心
d
参数化的确切类型。只是,这是个可怕的主意

{-# LANGUAGE GADTs #-}

import Unsafe.Coerce

data D a = D a deriving (Show)

data Wrap where       -- this GADT is equivalent to your `ExistentialQuantification` version
   Wrap :: D a -> Wrap

unwrap :: Wrap -> D a
unwrap (Wrap (D a)) = D (unsafeCoerce a)

main = print (unwrap (Wrap $ D "bla") :: D Integer)
这就是我执行简单程序时发生的情况:

依此类推,直到内存消耗导致系统停机

类型很重要如果你避开了类型系统,你就避开了程序的任何可预测性(即任何事情都可能发生,包括或著名的)


现在,显然你认为类型的工作方式不同。在动态语言(如Python)中,以及在某种程度上在面向对象语言(如Java)中,类型在某种意义上是值可以拥有的属性。因此,(参考-)值不仅包含区分单个类型的不同值所需的信息,还包含区分不同(子)类型的信息。这在很多方面都相当低效——这是Python速度如此之慢而Java需要如此巨大VM的一个主要原因

在Haskell中,类型在运行时不存在。函数永远不知道它使用的值的类型。只是因为编译器知道它将拥有的所有类型,所以函数不需要任何此类知识–编译器已经硬编码了它!(也就是说,除非你用
unsafectorce
来规避它,正如我所演示的那样,这听起来很不安全。)

如果您确实希望将类型作为“属性”附加到值,则需要显式地执行此操作,这就是存在包装器的用途。然而,通常有更好的方法在函数式语言中实现。你想要它的真正用途是什么



也许回忆一下多态结果签名的含义也会有所帮助
unwrap::Wrap->da
并不意味着“结果是一些
da
…调用者最好不要在意使用的
a
”。Java中就是这样,但在Haskell中却毫无用处,因为对于未知类型的值,您无能为力

相反,它意味着:对于调用方请求的任何类型的
a
,此函数都能够提供合适的
da
值。当然,这很难实现——如果没有额外的信息,这就像使用给定未知类型的值做任何事情一样不可能。但是,如果参数中已经有
a
值,或者
a
以某种方式被限制为类型类(例如
fromInteger::Num a=>Integer->a
,那么这是非常可能的,也是非常有用的


要获得一个
字符串
字段–独立于
a
参数–您可以直接对包装的值进行操作:

data D a = D
    { dLabel :: String
    , dValue :: a
    }

data Wrap where Wrap :: D a -> Wrap

labelFromWrap :: Wrap -> String
labelFromWrap (Wrap (D l _)) = l

要更一般地在
Wrap
上编写此类函数(使用任何“不关心
a
”的标签访问),请使用Rank2多态性,如n.m.的回答所示。

是的,可以,但不能以简单的方式

{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE RankNTypes #-}

data D a = D a

data Wrap = forall a. Wrap (D a)

unwrap :: Wrap -> forall r. (forall a. D a -> r) -> r
unwrap (Wrap x) k = k x

test :: D a -> IO ()
test (D a) = putStrLn "Got a D something"

main = unwrap (Wrap (D 5)) test

你不能从你的函数中返回一个
dasomething\u unknown
,但是你可以提取它并立即将它传递给另一个接受
daa
的函数,如图所示。

@n.m.因为OP要求它。我看不出OP在哪里询问
unsecfectorce
“你对未知类型的值无能为力。”--类型并非完全未知,编译器知道它是a
存在a.da
,因此,据我所知,应该能够为所有a应用函数
。(da->E)
请不要在回答哈斯凯尔问题时使用
不安全词
。这不是语言的一部分,应该不惜一切代价避免使用。@leftroundout对于一个显然是初学者的人来说,我永远不会提到丑陋和不安全的事情。我认为
存在的原因a.D a
不是哈斯凯尔的一部分,原因很简单希望避免冗余。这种类型相当于所有r.(所有a.D a->r)的
->r
,所以我们也可以用它来表示存在值。@n.m.严格地说,它不是等价的,但我同意,对这种存在值唯一有用的方法是对所有a.D a->r
应用函数
,所以无法表达这种特殊的存在值是有意义的——谢谢。