在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
,以便Major
和Minor
构造函数只能构造具有适当种类的变体的值:
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