Haskell 中所有 a(正常值)的新类型?

A new type for all a (normal value) and nothing in Haskell?

我要

  1. 创建一个新的类型和值来指示 none 就像 MaybeNothing
  2. 为所有 a(正常值)和 none
  3. 创建一个新的联合类型
  4. 创建一个接受新联合类型的函数,然后如果参数是 none 什么都不做。

不使用 Maybe,我如何在 Haskell 中执行此操作?

在打字稿中

  const none = { undefined };
  type None = typeof none;
  type Option<T> = None | T;
 
  type f = <T>(a:Option<T>) => any; 
  const f:f = a =>
    a === none
      ? undefined
      : console.log(a);

如何在 Haskell 中执行此操作?为了以防万一,请注意,我不是在问如何使用 Maybe Monad。谢谢。

如果你有一个你试图解决的实际编程问题,它需要你表示“要么是 T 类型的值,要么什么都不是”的概念,那么 Haskell 的答案是使用 Maybe T.

无论您的代码的目标是什么,如果在 Typescript 中您会使用 Option 来实现该目标,那么在 Haskell 中您可以使用 Maybe 来实现该目标。代码会有些不同,但这并不奇怪,因为它们是不同的语言。

但是,如果您尝试直接使用 Typescript 中涉及的概念 Option,在 Haskell 中实现一个完全相同的类型(而不是作为一个解决问题的工具),那么你就倒霉了。 Haskell 没有 联合类型的概念,也没有很容易模拟 one1.[=65 的方法=]

你可以 sort-of 试试 Typeable:

import Data.Typeable (Typeable, cast)

data None = None

f :: (Typeable a, Show a) => a -> IO ()
f x = case (cast x :: Maybe None)
        of Just None -> pure ()
           Nothing -> print x

当我们有一个 Typeable 约束在起作用时,我们实际上可以检查一个值是否属于某种类型(如果是,则在引用具有正确类型的地方获取对它的引用,所以我们可以将其用作该类型)。但这迫使我们无论如何都要通过 Maybe,因为 cast 需要一种方法来表示未成功的转换的值!

这也很棘手,因为现在我们的 None 类型的实际参数根本无法表示,而 Maybe 选项可以很好地表示 Just Nothing 。的确,您 经常 需要这种“嵌套 failure-or-absence” 功能(尽管绝不是永远不会;想想一个可能不会 return 结果的查询: 如果您需要区分 运行 查询失败与 return 查询无结果,那很自然 Maybe (Maybe Result))。但我更喜欢我处理任何类型的设施来处理 any 类型,具有一致性。根据设计,如果没有任何极端情况,您就不会被奇怪的极端情况所困扰。

而且您不能更一般地使用 Typeable 来实际声明一个类型,该类型可能是两个特定类型的联合;你必须接受带有 Typeable 约束的类型变量 a,这意味着人们可以传递给你 any 类型,而不仅仅是你试图放入的两个一个工会。由于您只能编写有限数量的 casts,因此 Typeable 从根本上只能用于专门处理一组特定类型,或者其他任何内容的代码路径(可能会出错) .

作为更一般的观点,您需要避免使用 over-using Typeable,以便编写能够获得 Haskell 类型系统的所有好处的代码。许多 Haskell 代码使用的属性产生于您 不能 对多态类型执行的操作,因为您不知道它是哪种具体类型。当 Typeable 正在播放时,所有这些都在 window 之外。举一个非常简单的例子,有一个经典的论点,即只有一个 a -> a 类型的函数(不会触底);由于无法知道类型 a 是什么,该函数的实现无法无中生有,因此它只能 return 未修改。但是函数类型 Typeable a => a -> a 可以有任意数量的古怪规则编码,例如“如果参数是 Integer,则向其添加一个”。无论实现中隐藏了什么,它都不会反映在 Typeable 约束之外的类型中,因此您必须熟悉实现的细节才能使用此函数,编译器不会发现你是否犯了错误。这种效果的一个明显例子是,在我尝试编写上面的 f 时,我们最终得到了 f :: Typeable a => a -> IO () 类型;有 zero 指示它期望使用的类型(它想要处理“None 或任何其他”)。

作为一个完全不同的轨道,你当然可以这样做:

data None = None
data Option a = Some a | None' None

现在您已经“创建了一个不代表任何内容的新类型”和“为所有 a(正常值)和 none 创建了一个新的联合类型”。但是 Haskell 的 ADT 是 discrimated 联合,这意味着在每个变体上总会有一个构造函数标签(在 运行 时间和你的源代码中)工会。

所以使用单独的 None 类型真的没有意义,因为一旦您在 Option a 值中看到 None' 标记,您就已经知道所有需要知道的了关于价值;查看 None 值,发现它是 None 并没有告诉您任何信息 2。所以你不妨使用:

data Option a = Some a | None

与内置 Maybe 完全相同,仅在选择的名称上有所不同。

类似地,您显然可以编写一个函数,其中包含一个 OptionNone 时要处理的案例,以及一个有内容时要处理的案例(其中任何一个都可以“什么都不做” ,如果您 return 是一种“做某事”有意义的类型,即某种动作,例如 IOState).

f :: Show a => Option a -> IO a
f None = pure ()
f (Some a) = print a

代码略有不同,甚至忽略了琐碎的语法和命名问题;我们ad 以引用 Some 构造函数并在 中的值 上调用 print,而不是直接将参数打印到 f 3。 Haskell 使用类型类将类型分为可以有意义地打印的类型和不能打印的类型,并且只允许您在前者上调用 print。但它是如此接近,以至于我毫无保留地说“这相当于 Haskell 你写的 Typescript”。

鉴于 Maybe 已经存在,您不妨使用它。 (同样,如果你需要它,内置类型 () 除了名称之外 - 与 data None = None 相同。)但是再说一次:如果你试图解决一个问题,那就是你要做的你会在 Typescript 中使用 Option for;相反,如果您的目标是实现与 Option 完全相同的东西 ,那么您根本无法使用 Haskell 提供的工具。


1 你可以 可能 使用不安全的功能(比如 unsafeCoerce 往返 Any).我不知道如何才能使它可用和可靠,这样做对于任何 实用 目的来说都是浪费时间;你永远不会在真正的程序中使用这样的代码,这只是一个有趣的练习,你可以在多大程度上破解语言实现。所以我不会写一个解决这个角度的答案。


2 嗯,从技术上讲,它告诉你产生它的计算终止了;它可能是底部。但是你不能用这些信息做任何事情,因为不可能 test 它是否是底部;如果是,你也会出错(或不终止)。


3 将整个参数打印到 f 也可以,它只会打印出来,例如Some "value",我想这不是你的意思。