type CP = Int :+: String :+: Boolean :+: CNil
val example = Coproduct[CP]("foo")
然后,您可以使用所有有趣的poly魔术来Map它们或执行其他操作:
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"
type MAYBE x = Sum '[(), x]
pattern NOTHING :: MAYBE x
pattern NOTHING = Ze :- ()
pattern JUST :: x -> MAYBE x
pattern JUST x = Su Ze :- x
甚至可以使用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)
6条答案
按热度按时间dm7nw8vv1#
这些被称为联积,实际上
Either
只是一个2参数的联积。您可以使用shapeless库中的helper来构建任意长度的联积,方法如下:然后,您可以使用所有有趣的
poly
魔术来Map它们或执行其他操作:据我所知,Scala.JS + Shapeless可以一起工作,所以这可能会给予你你想要的。
t5zmwmid2#
您使用的是哪种语言?如果是F#,则有一个三路
Choice<'T1,'T2,'T3>
type。(除了更“标准”的双向类型之外,还有4路、5路、6路和7路Choice类型)。wqnecbli3#
对于scala,有来自Scalaz的
Either3
:https://github.com/scalaz/scalaz/blob/scalaz-seven/core/src/main/scala/scalaz/Either3.scalaomjgkv6w4#
从另一个问题复制我的答案的后半部分:accept multiple types for a parameter in scala。
经过一些修改后,这里有一个当我们必须接受多个类型时的解决方案:
kq4fsx7k5#
我认为像这样严重依赖类型是一种反模式。
使用代数数据类型最好的一点是,结果类型可以告诉您所使用的域的一些信息。使用
Choice<T1, T2, T3>
这样的泛型类型,您实际上并没有说任何关于域的信息。我想是
option<T>
(akaMaybe
)非常清楚,因为它表示类型为T
的值要么存在,要么由于某种原因丢失。我认为Either<'T, exn>
仍然非常清楚,因为它表示您得到一个值或异常。但是,当您有两个以上的情况时,因此显式地定义一个具有与域匹配的名称的类型可能是一个好主意。(我偶尔会在F#中使用
Choice<T1, T2, T3>
,但这种用法通常局限于一个小范围内--不到50行代码--这样我就可以很容易地找到使用它的代码周围的含义。)bjp0bcyl6#
在最近的 haskell ,我会打开一个厨房Flume位。
然后定义类型级列表成员资格
现在我已经有了所有的有限和,而不需要写出一大堆OneOfN类型的定义:
但是,为了解决Tomas关于可读性的问题,我会使用模式同义词。事实上,这类事情就是我多年来一直在谈论模式同义词的原因。
你可以有一个有趣的版本
Maybe
:甚至可以使用
newtype
来构建递归求和。newtype Package 器还允许您为以这种方式构建的类型进行
instance
声明。当然,您也可以使用模式同义词来很好地隐藏迭代的
Either
。此技巧并不专用于求和:你也可以为产品做这个,这和de弗里斯和Löh的Generics-SOP库差不多。
这种编码的最大优势在于,数据的描述本身就是(类型级)数据,允许您在不修改编译器的情况下编写大量
deriving
风格的功能。将来(如果我有办法的话),所有的数据类型都将是 defined,而不是 declared,数据类型描述由指定数据的代数结构(允许计算通用设备)及其外观的数据组成(这样您就可以在处理特定类型时看到您在做什么)。
但未来已经在这里了。