Last active
August 29, 2015 14:12
-
-
Save panesofglass/8b8ac1db9dc89e190bb9 to your computer and use it in GitHub Desktop.
Experimenting with a potential solution for implementing protocols in F#
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#nowarn "0686" | |
[<AbstractClass>] | |
type ListLike<'T>() = | |
abstract Head : 'T with get | |
abstract Tail : ListLike<'T> with get | |
abstract Length : int | |
static member op_Implicit(list: 'T list) = | |
{ new ListLike<'T>() with | |
member __.Head = List.head list | |
member __.Tail = ListLike.op_Implicit(List.tail list) | |
member __.Length = List.length list } | |
static member op_Implicit(seq: 'T seq) = | |
{ new ListLike<'T>() with | |
member __.Head = Seq.head seq | |
member __.Tail = ListLike.op_Implicit(seq |> Seq.skip 1 |> Seq.cache) | |
member __.Length = Seq.length seq } | |
static member op_Implicit(array: 'T[]) = | |
{ new ListLike<'T>() with | |
member __.Head = array.[0] | |
member __.Tail = ListLike.op_Implicit(array.[1..]) | |
member __.Length = array.Length } | |
static member op_Implicit(option: 'T option) = | |
{ new ListLike<'T>() with | |
member __.Head = match option with | Some x -> x | None -> failwith "No value" | |
member __.Tail = match option with | Some x -> ListLike.op_Implicit None | None -> failwith "No value" | |
member __.Length = match option with | Some x -> 1 | None -> 0 } | |
module ListLike = | |
let head (x: ListLike<_>) = x.Head | |
let tail (x: ListLike<_>) = x.Tail | |
let length (x: ListLike<_>) = x.Length | |
ListLike.head [1;2;3;4] |> printfn "%i" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#nowarn "0686" | |
[<AbstractClass>] | |
type ListLike<'T>() = | |
abstract Head : 'T with get | |
abstract Tail : ListLike<'T> with get | |
abstract Length : int | |
static member op_Implicit(list: 'T list) = | |
{ new ListLike<'T>() with | |
member __.Head = List.head list | |
member __.Tail = ListLike.op_Implicit(List.tail list) | |
member __.Length = List.length list } | |
static member op_Implicit(seq: 'T seq) = | |
{ new ListLike<'T>() with | |
member __.Head = Seq.head seq | |
member __.Tail = ListLike.op_Implicit(seq |> Seq.skip 1 |> Seq.cache) | |
member __.Length = Seq.length seq } | |
static member op_Implicit(array: 'T[]) = | |
{ new ListLike<'T>() with | |
member __.Head = array.[0] | |
member __.Tail = ListLike.op_Implicit(array.[1..]) | |
member __.Length = array.Length } | |
static member op_Implicit(option: 'T option) = | |
{ new ListLike<'T>() with | |
member __.Head = match option with | Some x -> x | None -> failwith "No value" | |
member __.Tail = match option with | Some x -> ListLike.op_Implicit None | None -> failwith "No value" | |
member __.Length = match option with | Some x -> 1 | None -> 0 } | |
module ListLike = | |
let inline cast x = (^a : (static member op_Implicit : 'b -> ^a) x) | |
let head x = | |
let interim = cast<_, ListLike<_>> x | |
interim.Head | |
ListLike.head [1;2;3;4] |> printfn "%i" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#nowarn "0686" | |
let inline cast x = (^a: (static member Cast : 'b -> ^a) x) | |
[<AbstractClass>] | |
type ListLike<'T>() = | |
abstract Head : 'T with get | |
abstract Tail : ListLike<'T> with get | |
abstract Length : int | |
// Ideal would be to use op_Implicit rather than Cast, but op_Implicit can only be added on the original type definition. | |
static member Cast(list: 'T list) = | |
{ new ListLike<'T>() with | |
member __.Head = List.head list | |
member __.Tail = ListLike.Cast(List.tail list) | |
member __.Length = List.length list } | |
static member Cast(seq: 'T seq) = | |
{ new ListLike<'T>() with | |
member __.Head = Seq.head seq | |
member __.Tail = ListLike.Cast(seq |> Seq.skip 1 |> Seq.cache) | |
member __.Length = Seq.length seq } | |
static member Cast(array: 'T[]) = | |
{ new ListLike<'T>() with | |
member __.Head = array.[0] | |
member __.Tail = ListLike.Cast(array.[1..]) | |
member __.Length = array.Length } | |
module Option = | |
type ListLike<'T> with | |
static member Cast(option: 'T option) = | |
{ new ListLike<'T>() with | |
member __.Head = match option with | Some x -> x | None -> failwith "No value" | |
member __.Tail = match option with | Some x -> ListLike.Cast None | None -> failwith "No value" | |
member __.Length = match option with | Some x -> 1 | None -> 0 } | |
let listCast = cast<_, ListLike<_>> [1;2;3;4] | |
listCast.Head |> printfn "%i" | |
listCast.Tail.Tail.Head |> printfn "%i" | |
let arrayCast = cast<_, ListLike<_>> [|1;2;3;4|] | |
arrayCast.Head |> printfn "%i" | |
listCast.Tail.Tail.Head |> printfn "%i" | |
let seqCast = seq { yield 1; yield 2; yield 3; yield 4 } |> cast<_, ListLike<_>> | |
seqCast.Head |> printfn "%i" | |
listCast.Tail.Tail.Head |> printfn "%i" | |
let optCast = Some 1 |> cast<_, ListLike<_>> | |
seqCast.Head |> printfn "%i" | |
listCast.Tail.Tail.Head |> ignore // will throw |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Sadly, statically resolved type parameters only appear to work on members defined on the type and do not pick up extension members. Therefore, the cast in line 53 will fail to compile. This is effectively the same issue as not allowing
op_Implicit
to be defined outside the type definition.