Type level number arithmetic

两盒软妹~` 提交于 2019-12-11 02:33:52

问题


I'm playing around with F#'s type inferrer. Trying to get type level natural number working.

Here's the part that I managed to get working

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>>

The whole instance part is the same hack that makes FsControl possible. This file is also a part of my project.

So far this works. I can do things like

let a = inc Zero |> inc |> inc |> inc |> dec |> dec |> dec
let b = dec a
let x = zero a
let y = zero b

And x and y get correctly inferred to False and True respectively (with a being inferred to Succ and b being Zero).

Now the tricky part. I want to make the function add. It needs to be able to take both Succ and Zero, so again I need to use the overloading hack to make it work.

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>) ()  

And as far as I can tell, this should work. Shouldn't it? But it doesn't. I get an error on the Inline.instance call saying the ambiguity can't be resolved with the information given prior to that point. I kind of get why, since I'm not in an inline function it tries to resolve to a concrete type, but it doesn't have one yet. But I still have a feeling that I should be able to make it work somehow.

Is there a way to make it work?


回答1:


The problem is that only one overload should match.

You don't need more than two, you can add the Zero case and the one that will loop, which by the way should be marked inline as stated in the comments:

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)()

But then there is another problem, at the 2nd overload it will default to Zero, inferred from the 1st overload.

One trick to solve this is to add a dummy overload:

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)()

That way it will not default to Zero since it can't be inferred at compile time.

I did also an implementation of type level numbers some time ago, using overloaded operators and pattern matching.

UPDATE

You don't need the second argument to be polymorphic, this will also do the job:

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>>

There are some differences between your implementation and the one I did (see the link) but I wouldn't use the inline helper, at least as it is defined in FsControl since it was designed specifically to infer based also in the return type.



来源:https://stackoverflow.com/questions/25723416/type-level-number-arithmetic

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!