Recent FPish FPish discussion focused on some hacks available in F# to write code that resembles using Haskell type classes. I particularly enjoyed the comments by Gustavo Leon and Loic Denuziere.
To cut the long story short, before compiling to .NET F# expands methods delcared inline and does overload resolution. This was intended to support flexible operator overloading, but opens up the door for interesting hacks. Even code that generalizes over higher kinds and therefore cannot exist at .NET level can with these hacks exist at the inline F# level.
Although these remain hacks (ugly code, unreadable inferred types, fragility to changes when type inference stops working, difficulty in scaling to more complicated examples), I cannot help but enjoy it. Here is a writeup that infers JSON serialization automatically over lists at the inline level:
type Json = | |
| Array of list<Json> | |
| Double of double | |
| False | |
| Integer of int64 | |
| Null | |
| Object of list<string*Json> | |
| String of string | |
| True | |
static member inline ToJson x : Json = | |
((^a or ^b) : | |
(static member ToJson : ^a * ^b -> Json) (Null, x)) | |
static member ToJson(_: Json, x: int) = Integer (int64 x) | |
static member ToJson(_: Json, x: string) = String x | |
static member inline ToJson(_: Json, x: list<_>) = | |
Array (List.map Json.ToJson x) | |
static member inline FromJson(x: Json) = | |
((^a or ^b) : (static member FromJson : ^a * ^b -> ^a) | |
(Unchecked.defaultof<_>, x)) | |
static member FromJson(_: int, x: Json) = | |
match x with | |
| Integer x -> int x | |
| _ -> invalidArg "x" "Conversion failed." | |
static member FromJson(_: string, x: Json) = | |
match x with | |
| String x -> x | |
| _ -> invalidArg "x" "Conversion failed." | |
static member inline FromJson(_: list<_>, x: Json) = | |
match x with | |
| Array xs -> [for x in xs -> Json.FromJson x] | |
| _ -> invalidArg "x" "Conversion failed." | |
type Person = | |
{ | |
age: int | |
name: string | |
} | |
static member ToJson(_: Json, x: Person) = | |
Object [ | |
"age", Integer (int64 x.age); | |
"name", String x.name | |
] | |
static member FromJson(_:Person, j: Json) = | |
match j with | |
| Object ["age", Integer age; "name", String name] -> | |
{ age = int age; name = name } | |
| _ -> | |
failwith "Conversion failed." | |
let test = | |
let p = {name="Vladimir Putin"; age=59} | |
let r : list<Person> = Json.FromJson(Json.ToJson [p; p; p]) | |
r |