在Haskell中,可以在类型推断期间引发类型错误,以对DSL施加类型约束。



class ValidHarmInterval (i :: IntervalType)
instance TypeError (Text "Minor seconds forbidden.")
=> ValidHarmInterval (Interval Min Second)
instance TypeError (Text "Major sevenths forbidden.")
=> ValidHarmInterval (Interval Maj Seventh)
instance {-# OVERLAPPABLE #-} ValidHarmInterval i


在OCaml中是否可能与此类似?

最佳答案

我不知道OCaml中的TypeError等效项,并且快速搜索没有发现任何明显的问题。但是我可以想到两种实现您想要的效果的方法:对DSL实施类型约束。

键入最终(无标签-最终)样式的EDSL

您可能需要检查Oleg嵌入的DSL的"typed final" style。它似乎恰好具有您想要的属性:


如果要键入的DSL也被键入,则最终键入的方法特别有吸引力。然后,我们不仅可以用宿主语言表示术语,而且可以表示DSL的类型系统(类型派生)。只能嵌入类型良好的DSL术语。


教程Modular, composable, typed optimizations in the tagless-final style提供了有关使用OCaml编写这种样式的EDSL的分步说明。

多态类型变体

OCaml提供了另一种更轻量级的(但可能表现不佳?)的方法来对这种子类型关系施加类型约束:polymorphic variants

因此,我们可以使用多态变体为间隔定义类型:

type 'a interval =
  [< `Unison
  | `Second
  | `Third
  | `Fourth
  | `Fifth
  | `Sixth
  | `Seventh
  | `Octave
  ] as 'a


其中,<表示变体的任何子集都可以构造类型为'a interval的值(暂时忽略类型变量)。

因此,我们可以编写一个将_ interval转换为string的标准函数,并按预期将其输入为_ interval -> string

let interval_to_string : _ interval -> string = function
  | `Unison  -> "Unison"
  | `Second  -> "Second"
  | `Third   -> "Third"
  | `Fourth  -> "Fourth"
  | `Fifth   -> "Fifth"
  | `Sixth   -> "Sixth"
  | `Seventh -> "Seventh"
  | `Octave  -> "Octave"


但是我们也可以定义一个仅接受_ interval类型的值的函数:

let even_interval_to_int : _ interval -> int = function
  | `Second -> 2
  | `Fourth -> 4
  | `Sixth  -> 6
  | `Octave -> 8


interval_to_string一样,even_interval_to_int也是_ interval类型的值的函数,但是如果将其应用于不受支持的时间间隔,则类型检查将失败:

let invalid_int_of_even_interval = even_interval_to_int `Third
(* Error: This expression has type [> `Third ]
 *        but an expression was expected of type
 *          [< `Fourth | `Octave | `Second | `Sixth ]
 *        The second variant type does not allow tag(s) `Third *)


这是因为[< Fourth |八度| Second | Sixth]是_ interval的子类型。

转到您的示例(请原谅我对音乐理论的无知),我们可以将次谐波和次谐波间隔编码为相交但_ interval的不同子集:

type major_harmonic_interval =
  [ `Unison
  | `Second
  | `Third
  | `Fourth
  | `Fifth
  | `Sixth
  (* No Seventh *)
  | `Octave
  ]

type minor_harmonic_interval =
  [ `Unison
  (* No Second*)
  | `Third
  | `Fourth
  | `Fifth
  | `Sixth
  | `Seventh
  | `Octave
  ]


然后约束我们的type harmonic_interval,以便MajorMinor构造函数只能构造具有适当种类的变体的值:

type harmonic_interval =
  | Major of major_harmonic_interval
  | Minor of minor_harmonic_interval


这将使我们构造所需的谐波间隔:

let major_second = Major `Second


但是强迫类型系统禁止我们没有的任何谐波间隔

let minor_second = Minor `Second
(* Error: This expression has type [> `Second ]
 *        but an expression was expected of type minor_harmonic_interval
 *        The second variant type does not allow tag(s) `Second *)


同时,我们仍然可以利用我们编写的对_ interval类型的值进行操作的函数:

let harmonic_interval_to_string : harmonic_interval -> string = function
  | Major interval -> "Major " ^ interval_to_string interval
  | Minor interval -> "Minor " ^ interval_to_string interval

09-17 11:39
查看更多