Haskell中是否有未标记联合类型的编译器扩展?

jcc333

在某些语言中(例如,#racket / typed),程序员可以指定联合类型而又不歧视它,例如,该类型(U Integer String)捕获整数和字符串,而无需(I Integer) (S String)以某种data IntOrStringUnion = ...形式或类似形式对其进行标记有没有办法在Haskell中做同样的事情?

克里斯蒂安·康克尔

Either 是您想要的... ish。

用Haskell术语,我将以匿名求和类型描述您要查找的内容匿名,我的意思是它没有定义的名称(如带有data声明的名称)。求和类型是指可以具有几种(可区分)类型之一的数据类型。带有标签的工会等。(如果您不熟悉此术语,请尝试使用Wikipedia作为入门。)

我们有一个众所周知的惯用匿名产品类型,它只是一个元组。如果要同时使用Inta和a String,只需用逗号将它们混在一起即可:(Int, String)元组(看似)可以永远持续下去(Int, String, Double, Word),并且您可以以相同的方式进行模式匹配。(有一个限制,但是没关系。)

众所周知的惯用匿名类型为Either,来自Data.Either(和Prelude):

data  Either a b  =  Left a | Right b
  deriving (Eq, Ord, Read, Show, Typeable)

它有一些缺点,最突出的是在这种情况下以一种奇怪的方式进行Functor支持实例Right问题在于,链接会带来很多尴尬:类型最终会像Either (Int (Either String (Either Double Word)))正如其他人指出的那样,模式匹配更加尴尬。

我只想指出,我们可以更接近(我理解是)Racket用例。通过我非常简短的Googling,在Racket中看起来像可以使用函数isNumber?来确定联合类型的给定值中实际上是哪种类型。在Haskell中,我们通常使用案例分析(模式匹配)来进行此操作,但是,这样做很尴尬Either,并且使用简单模式匹配的函数可能最终会硬连接到特定的并集类型。我们可以做得更好。

IsNumber?

我将编写一个我认为是Haskell惯用语言的函数isNumber?首先,我们不喜欢进行布尔测试,然后再运行假定其结果的函数;取而代之的是,我们倾向于只转换到Maybe那里然后从那里去。因此,函数的类型将以结尾-> Maybe IntInt现在用作替身。)

但是箭头的左手边是什么?“可能是Int或String或我们放入联合中的任何其他类型的东西。” 嗯好吧 因此它将成为多种类型之一。这听起来像typeclass,所以我们将约束和类型变量放在箭头的左侧:MightBeInt a => a -> Maybe Int好的,让我们写出这个类:

class MightBeInt a where
  isInt   :: a -> Maybe Int
  fromInt :: Int -> a

好的,现在我们如何编写实例?好吧,我们知道第一个参数是否EitherInt,我们是金色的,所以我们将其写出来。(顺便说一句,如果您想做一个很好的练习,请仅查看instance ... where下面三个代码块各个部分,并尝试自己实现该类成员。)

instance MightBeInt (Either Int b) where
  isInt (Left i) = Just i
  isInt _ = Nothing
  fromInt = Left

美好的。同样,ifInt是第二个参数:

instance MightBeInt (Either a Int) where
  isInt (Right i) = Just i
  isInt _ = Nothing
  fromInt = Right

但是呢Either String (Either Bool Int)诀窍是递归右手类型:如果不是Int,那它MightBeInt本身就是实例吗?

instance MightBeInt b => MightBeInt (Either a b) where
  isInt (Right xs) = isInt xs
  isInt _ = Nothing
  fromInt = Right . fromInt

(请注意,所有这些都需要FlexibleInstancesOverlappingInstances。)花了我很长时间才可以编写和阅读这些类实例。如果这种情况令人惊讶,请不要担心。最重要的是,我们现在可以执行以下操作:

anInt1 :: Either Int String
anInt1 = fromInt 1

anInt2 :: Either String (Either Int Double)
anInt2 = fromInt 2

anInt3 :: Either String Int
anInt3 = fromInt 3

notAnInt :: Either String Int
notAnInt = Left "notint"

ghci> isInt anInt3
Just 3
ghci> isInt notAnInt
Nothing

伟大的!

泛化

好的,但是现在我们需要为每个要查找的类型编写另一个类型类吗?没有!我们可以通过要查找的类型来对类进行参数化!这是一个很机械的翻译;唯一的问题是如何告诉编译器我们正在寻找什么类型,这就是要解决的地方Proxy(如果您不想安装tagged或运行base4.7,只需定义data Proxy a = Proxy。这没什么特别的,但是您需要PolyKinds。)

class MightBeA t a where
  isA   :: proxy t -> a -> Maybe t
  fromA :: t -> a

instance MightBeA t t where
  isA _ = Just
  fromA = id

instance MightBeA t (Either t b) where
  isA _ (Left i) = Just i
  isA _ _ = Nothing
  fromA = Left

instance MightBeA t b => MightBeA t (Either a b) where
  isA p (Right xs) = isA p xs
  isA _ _ = Nothing
  fromA = Right . fromA

ghci> isA (Proxy :: Proxy Int) anInt3
Just 3
ghci> isA (Proxy :: Proxy String) notAnInt
Just "notint"

现在的可用性情况是……更好。顺便说一句,我们丢失的主要内容是穷举性检查器。

与符号平价 (U String Int Double)

为了好玩,我们可以在GHC 7.8中使用DataKindsTypeFamilies消除中缀类型构造函数,而使用类型级别列表。(在Haskell中,不能有一个类型构造函数,例如-IO或-Either不能使用可变数量的参数,但是类型级别列表只是一个参数。)只有几行,我并不是很想解释:

type family OneOf (as :: [*]) :: * where
  OneOf '[] = Void
  OneOf '[a] = a
  OneOf (a ': as) = Either a (OneOf as)

请注意,您需要导入Data.Void现在我们可以这样做:

anInt4 :: OneOf '[Int, Double, Float, String]
anInt4 = fromInt 4

ghci> :kind! OneOf '[Int, Double, Float, String]
OneOf '[Int, Double, Float, String] :: *
= Either Int (Either Double (Either Float [Char]))

换句话说,OneOf '[Int, Double, Float, String]与相同Either Int (Either Double (Either Float [Char]))

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

编译器是否未实现“使用类型名”指令?

来自分类Dev

TypeScript 编译器认为具有类型索引的递归联合类型是字符串?

来自分类Dev

TypeScript编译器无法从联合类型的参数中消除过滤的类型

来自分类Dev

联合优化中的分段错误与编译器优化

来自分类Dev

联合优化中的分段错误与编译器优化

来自分类Dev

gcc是否具有用于定义文件类型/编译器的编译指示?

来自分类Dev

msbuild是否带有编译器?

来自分类Dev

编译器是否有区别-Java

来自分类Dev

返回类型为“ <T扩展...>”的意外编译器警告

来自分类Dev

为什么编译器无法在Haskell中为我们处理新类型?

来自分类Dev

浮点数是否有特定于Microsoft的(用于Microsoft编译器)数据类型

来自分类Dev

是否可以为Haskell编写即时(JIT)编译器?

来自分类Dev

编译器未实现“使用类型名”指令吗?

来自分类Dev

如何使用TS编译器API查看.d.ts中的类型是否已更改?

来自分类Dev

编译器是否为未使用的类生成vtable

来自分类Dev

打字稿告知编译器正在使用哪种联合类型

来自分类Dev

协议扩展编译器中的Swift 2.2 #selector错误

来自分类Dev

是否有内置JSX编译器的浏览器?

来自分类Dev

Visual Studio中的代码块中是否有类似于编译器标志的模拟物?

来自分类Dev

有关gcc中的punning类型的编译器警告

来自分类Dev

表示形式编译器:方法调用中的类型完成带有多个参数

来自分类Dev

有关gcc中的punning类型的编译器警告

来自分类Dev

没有类型转换的 Java 编译器如何在子类中调用正确的 equals() 函数?

来自分类Dev

Java中是否有一种方法可以让编译器处理互斥选项?

来自分类Dev

如何在Firebase Bolt编译器中检查用户是否拥有对象

来自分类Dev

Rust中是否有“优化调试体验”编译器标志?

来自分类Dev

是否有可能从lessphp编译器中获得更少的CSS?

来自分类Dev

无法编译Glasgow Haskell编译器

来自分类Dev

g ++编译器中未使用的变量

Related 相关文章

  1. 1

    编译器是否未实现“使用类型名”指令?

  2. 2

    TypeScript 编译器认为具有类型索引的递归联合类型是字符串?

  3. 3

    TypeScript编译器无法从联合类型的参数中消除过滤的类型

  4. 4

    联合优化中的分段错误与编译器优化

  5. 5

    联合优化中的分段错误与编译器优化

  6. 6

    gcc是否具有用于定义文件类型/编译器的编译指示?

  7. 7

    msbuild是否带有编译器?

  8. 8

    编译器是否有区别-Java

  9. 9

    返回类型为“ <T扩展...>”的意外编译器警告

  10. 10

    为什么编译器无法在Haskell中为我们处理新类型?

  11. 11

    浮点数是否有特定于Microsoft的(用于Microsoft编译器)数据类型

  12. 12

    是否可以为Haskell编写即时(JIT)编译器?

  13. 13

    编译器未实现“使用类型名”指令吗?

  14. 14

    如何使用TS编译器API查看.d.ts中的类型是否已更改?

  15. 15

    编译器是否为未使用的类生成vtable

  16. 16

    打字稿告知编译器正在使用哪种联合类型

  17. 17

    协议扩展编译器中的Swift 2.2 #selector错误

  18. 18

    是否有内置JSX编译器的浏览器?

  19. 19

    Visual Studio中的代码块中是否有类似于编译器标志的模拟物?

  20. 20

    有关gcc中的punning类型的编译器警告

  21. 21

    表示形式编译器:方法调用中的类型完成带有多个参数

  22. 22

    有关gcc中的punning类型的编译器警告

  23. 23

    没有类型转换的 Java 编译器如何在子类中调用正确的 equals() 函数?

  24. 24

    Java中是否有一种方法可以让编译器处理互斥选项?

  25. 25

    如何在Firebase Bolt编译器中检查用户是否拥有对象

  26. 26

    Rust中是否有“优化调试体验”编译器标志?

  27. 27

    是否有可能从lessphp编译器中获得更少的CSS?

  28. 28

    无法编译Glasgow Haskell编译器

  29. 29

    g ++编译器中未使用的变量

热门标签

归档