类型级别编号算术

时间:2014-09-08 11:38:56

标签: f#

我正在玩F#的类型推导者。试图让类型级自然数工作。

这是我设法开始工作的部分

type Zero = Zero

type Succ<'a> = None

type True = True

type False = False

type IsZero = 
    | IsZero
    static member instance (IsZero, _ : Succ<'a>, _) = fun () -> False
    static member instance (IsZero, _ : Zero, _) = fun () -> True

module TypeArithmetic = 
    let inline zero x = (Inline.instance (IsZero, x) : unit -> 'a)()
    let dec (_ : Succ<'a>) = Unchecked.defaultof<'a>
    let inc (_ : 'a) = Unchecked.defaultof<Succ<'a>>

整个instance部分与使FsControl成为可能的hack相同。这个file也是我项目的一部分。

到目前为止这是有效的。我可以做像

这样的事情
let a = inc Zero |> inc |> inc |> inc |> dec |> dec |> dec
let b = dec a
let x = zero a
let y = zero b

x和y分别被正确推断为False和True(推断为Succ,b为零)。

现在是棘手的部分。我想创建函数add。 它需要能够同时使用Succ和Zero,所以我需要使用重载hack来使其工作。

type Add =
    | Add
    static member instance (Add, _ : Zero, _ : Zero, _) = fun () -> Zero
    static member instance (Add, x : Succ<'a>, _ : Zero, _) = fun () -> x
    static member instance (Add, _ : Zero, y : Succ<'a>, _) = fun () -> y
    static member instance (Add, _ : Succ<'a>, _ : Succ<'b>, _) = 
        fun () -> Inline.instance(Add, Unchecked.defaultof<Succ<Succ<'a>>>, Unchecked.defaultof<'b>) ()  

据我所知,这应该有效。不应该吗?但它并没有。我在Inline.instance电话上收到错误消息,说明在此之前给出的信息无法解决歧义。我有点理解,因为我没有在内联函数中尝试解析为具体类型,但它还没有。但我仍然觉得我应该能够以某种方式使它发挥作用。

有没有办法让它发挥作用?

1 个答案:

答案 0 :(得分:2)

问题是只有一个重载匹配。

您不需要超过两个,您可以添加Zero案例和将循环的案例,顺便说一下,应该按照评论中的说明内联标记:

type Add =
    | Add
    static member        instance (Add, _ :Zero    , y   , _) = fun () -> y
    static member inline instance (Add, _ :Succ<'a>, _:'b, _) = fun () -> 
        Inline.instance(Add, Unchecked.defaultof<'a>, Unchecked.defaultof<Succ<'b>>) ()

let inline add x y = Inline.instance (Add, x, y)()

但是还有另外一个问题,在第二次超载时它会默认为零,从第一次过载推断出来。

解决此问题的一个技巧是添加虚拟重载:

type Add =
    | Add
    static member        instance (Add, _ :Add      , y  , _) = fun () -> y
    static member        instance (Add, _ :Zero     , y  , _) = fun () -> y
    static member inline instance (Add, _ :Succ<'a> ,_:'b, _) = fun () -> 
        Inline.instance(Add, Unchecked.defaultof<'a>, Unchecked.defaultof<Succ<'b>>) ()

let inline add x y = Inline.instance (Add, x, y)()

这样它就不会默认为零,因为它无法在编译时推断出来。

前段时间我也使用重载运算符和模式匹配来做implementation of type level numbers

<强>更新

你不需要第二个参数是多态的,这也可以做到这一点:

type Add =
    | Add
    static member        instance (Add, _ :Add     , _) = id
    static member        instance (Add, _ :Zero    , _) = id
    static member inline instance (Add, _ :Succ<'a>, _) = fun (_:'b) -> 
        Inline.instance(Add, Unchecked.defaultof<'a>) Unchecked.defaultof<Succ<'b>>

您的实现与我所做的之间存在一些差异(请参阅链接),但我不会使用内联帮助程序,至少在FsControl中定义,因为它是专门为推断而设计的返回类型。