如何在Haskell中基于输入值限制类型?

伊格穆特

我是Haskell的新手。抱歉,这个问题的答案很明显。

我有

data Tmp = Foo Int
         | Bar Int
         | Baz Int

data Test = A Tmp Tmp

构造函数A Tmp Tmp可以采用任何构造函数,Tmp除了A (Baz i) (Baz j)whereijare any Int有没有什么办法可以限制第二TmpA Tmp TmpBaz当第一Tmp已经是Baz

毒菌属

答案取决于您希望如何实施限制:在运行时或编译时。

要在运行时实施限制,您可以添加一个函数(例如makeA)来检查限制,然后调用构造函数。这样的函数会做一些事情,然后调用构造函数,也称为智能构造函数如果仅从模块导出智能构造函数makeA而不是实际构造函数A,则可以确保其他模块使用智能构造函数,因此始终检查限制。

例:

module Test (Tmp (Foo, Bar, Baz), Test (), makeA) where
  data Tmp
     = Foo Int
     | Bar Int
     | Baz Int

  data Test = A Tmp Tmp

  makeA :: Tmp -> Tmp -> Tmp
  makeA (Baz _) (Baz _) = error "makeA: two baz problem"
  makeA tmp1 tmp2 = A tmp1 tmp2

这种技术的好处是您根本不必更改数据类型。缺点是该限制仅在运行时强制执行。

要在编译时强制执行此限制,您需要以某种方式更改数据类型。当前数据类型的问题在于类型检查器无法区分由FooBar构造的值以及由构造的Baz对于类型检查器来说,这些都是Tmp值,因此类型检查器无法强制某些Tmp值可以,而其他值则不能。因此,我们必须更改数据类型以对类型中的Tmp值的“ Bazness”进行编码

将Bazness编码为该类型的一种方法是Tmp按照以下方式进行重组

data TmpNotBaz
  = Foo Int
  | Bar Int

data Tmp
  = NotBaz TmpNotBaz
  | Baz Int

现在很明显,类型的值TmpNotBaz不能为Baz,但是类型的值Tmp可以为Baz这种想法的好处是它仅使用基本的Haskell功能。一个次要的缺点是您需要将对的调用NotBaz放入代码中。一个主要的缺点是我们仍然不能直接表达“如果一个参数A可以是,Baz可以是其中一个”的想法我们将不得不编写多个版本的A

data Test
  = A1 TmpNotBaz Tmp
  | A2 Tmp TmpNotBaz

现在,我们可以通过选择A1A2根据需要来表达我们想要的所有值,而A (Baz ...) (Baz ...)不再能够根据需要来表达该解决方案的一个问题是,曾经有多种表示形式,例如A (Foo 1) (Foo 2)A1 (Foo 1) (NotBaz (Foo 2))A2 (NotBaz (Foo 1)) (Foo 2)表示该值。

您可以尝试像这样处理数据类型的结构,并创建适合您情况的版本。

用于对类型中的Bazness进行编码的另一种方法是在类型上注释一些类型级别的信息,Tmp并使用类型级别的编程来对此类型级别的信息进行推理。这个想法的缺点是它使用了更高级的Haskell功能。实际上,有许多新兴的方法可以完成这种事情,但尚不清楚哪种方法最终将被视为“标准”高级Haskell。也就是说,这是一种方法:

{-# LANGUAGE GADTs, TypeFamilies, DataKinds #-}

data Bazness = IsBaz | NotBaz

data BothBazOrNot = BothBaz | NotBothBaz

type family AreBothBaz (b1 :: Bazness) (b2 :: Bazness) :: BothBazOrNot where
  AreBothBaz 'IsBaz 'IsBaz = 'BothBaz
  AreBothBaz _ _ = 'NotBothBaz

data Tmp (b :: Bazness) :: * where
  Foo :: Int -> Tmp 'NotBaz
  Bar :: Int -> Tmp 'NotBaz
  Baz :: Int -> Tmp 'IsBaz

data Test where
  A :: AreBothBaz b1 b2 ~ 'NotBothBaz => Tmp b1 -> Tmp b2 -> Test

注意构造函数的类型签名FooBarBaz讨论构造函数是创建IsBaz还是构造某些东西NotBaz而如何为类型签名A的一些谈判b1b2选择,使NotBothBaz

使用此代码,我们可以编写以下表达式:

  • A (Foo 1) (Bar 2)
  • A (Foo 1) (Baz 2)
  • A (Baz 1) (Bar 2)

但是,如果我们尝试编写A (Baz 1) (Baz 2),则类型检查器会抱怨:

Couldn't match type 'BothBaz with 'NotBothBaz
    arising from a use of A
 In the expression: A (Baz 1) (Baz 2)

因此,类型检查器发现在这种情况下,toA参数BothBaz,但是我们注释了typeA以仅接受are NotBothBaz,因此类型检查器抱怨BothBaz与有所不同NotBothBaz

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

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

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

如何基于类型化对象数组中的值限制类型?

如何基于对象中的值限制对象类型?

如何在Haskell的一行中输入多个值

如何在Haskell中获取'[String]的类型级别的值?

如何在jQuery中获取先前输入类型文本的值?

如何在Chrome中检查输入类型=“日期”的(空)值

如何在jQuery中检查输入类型的图像值

如何限制FsCheck中的输入值?

如何在javascript中限制多个输入

如何在html5输入类型Date中限制过去的日期

如何在模板中限制类型名

如何限制 Profile 以查看基于 Salesforce 中的记录类型的案例?

如何在Haskell中对类型类成员的返回值表达类型类约束?

如何在输入栏中输入限制字符

如何在基于Java中字符串值的方法中返回不同类型?

如何在 Java 中限制值?

如何使用JavaScript基于另一个输入的值限制输入

如何在Haskell中得出构图类型

如何在Haskell中触发类型错误?

如何在Haskell中定义数组类型

如何在父类型中限制子类型?

如何在Kotlin中基于多种类型和值进行排序?

如何在Orchard中基于其他内容类型值创建Projection

如何在iOS UISearchBar中限制搜索(基于键入速度)?

如何在openldap中基于IP地址限制用户

如何在Elasticsearch中基于输入字段获取字段的总和值(输入字段和总和输出字段不同)

如何在Java中输入BigInteger类型

如何在反应中从多个字段中获取相同输入类型的值?

如何在 Flutter 中限制文本输入中的特殊字符?