总和类型的标准名称如Either但是3个案例?

Tri*_*Gao 10 f# ocaml haskell scala typescript

是否存在类似Either的标准和类型,但有3种情况?Haskell有,These但不是那样.

Tom*_*cek 16

我认为严重依赖这种类型是一种反模式.

使用代数数据类型得到的最好的一点是结果类型会告诉您有关您正在使用的域的信息.对于类似的泛型类型Choice<T1, T2, T3>,您实际上并没有对域名做任何说明.

我认为option<T>(aka Maybe)非常清楚,因为它表示某种类型的值T存在或由于某种原因而丢失.我认为Either<'T, exn>仍然很清楚,它说你得到一个价值或一个例外.但是,当您遇到两个以上的案例时,很难理解案例的含义,因此明确定义名称与域匹配的类型可能是个好主意.

(我Choice<T1, T2, T3>偶尔使用F#,但用法通常仅限于小范围 - 少于50行代码 - 这样我就可以很容易地找到代码消耗它的含义.)


fla*_*ian 8

这些被称为联产品实际上Either只是一个 2 参数联产品。您可以使用 shapeless 库中的 helpers 来构建任意长度的联产品:

type CP = Int :+: String :+: Boolean :+: CNil

val example = Coproduct[CP]("foo")
Run Code Online (Sandbox Code Playgroud)

然后,您可以使用所有有趣的poly魔法来映射它们或执行其他操作:

object printer extends Poly1 {
  implicit def caseInt = at[Int](i => i -> s"$i is an int")
  implicit def caseString = at[String](s => s -> s"$s is a string")
  implicit def caseBoolean = at[Boolean](b => s -> s"$b is a bool")
}
val mapped = example map printer
mapped.select[(String, String)] shouldEqual "foo is a string"
Run Code Online (Sandbox Code Playgroud)

据我所知,Scala.JS + Shapeless 可以一起工作,所以这可能会给你你想要的。


pig*_*ker 8

在最近的Haskell中,我打开了一点厨房水槽.

{-# LANGUAGE PolyKinds, DataKinds, GADTs, KindSignatures,
    TypeOperators, PatternSynonyms #-}
Run Code Online (Sandbox Code Playgroud)

然后我将定义类型级列表成员资格

data (:>) :: [x] -> x -> * where
  Ze ::            (x ': xs) :> x
  Su :: xs :> x -> (y ': xs) :> x
Run Code Online (Sandbox Code Playgroud)

现在我拥有了所有有限的总和,而没有找出一大堆OneOfN类型定义:

data Sum :: [*] -> * where
  (:-) :: xs :> x -> x -> Sum xs
Run Code Online (Sandbox Code Playgroud)

但是,为了解决Tomas关于可读性的问题,我将使用模式同义词.事实上,这种事情是我多年来一直在讨论模式同义词的原因.

你可以有一个有趣的版本Maybe:

type MAYBE x = Sum '[(), x]

pattern NOTHING :: MAYBE x
pattern NOTHING = Ze :- ()

pattern JUST :: x -> MAYBE x
pattern JUST x = Su Ze :- x
Run Code Online (Sandbox Code Playgroud)

你甚至可以newtype用来建立递归总和.

newtype Tm x = Tm (Sum '[x, (Tm x, Tm x), Tm (Maybe x)])

pattern VAR :: x -> Tm x
pattern VAR x = Tm (Ze :- x)

pattern APP :: Tm x -> Tm x -> Tm x
pattern APP f s = Tm (Su Ze :- (f, s))

pattern LAM :: Tm (Maybe x) -> Tm x
pattern LAM b = Tm (Su (Su Ze) :- b)
Run Code Online (Sandbox Code Playgroud)

newtype包装器还允许您对instance以这种方式构建的类型进行声明.

当然,您也可以使用模式同义词来Either很好地隐藏迭代.

这种技术并不仅限于总和:你也可以为产品做到这一点,而这正是de Vries和Löh的Generics-SOP库中发生的事情.

这种编码的最大胜利是数据描述本身(类型级别)数据,允许您在deriving不破坏编译器的情况下烹饪许多样式的功能.

在未来(如果我有我的方式),所有数据类型将被定义,而不是声明,数据类型描述由数据组成,指定数据的代数结构(允许计算通用设备)及其外观(所以你可以看到你在使用特定类型时所做的事情).

但未来已经在这里了.


rmu*_*unn 3

您使用哪种语言?如果是F#,则有三路Choice<'T1,'T2,'T3>类型。(除了更“标准”的两路类型之外,还有 4、5、6 和 7 路选择类型)。