xyncro / chiron

JSON for F#
https://xyncro.tech/chiron
MIT License
175 stars 41 forks source link

Extend type from another assembly with serialisation functions? #16

Closed haf closed 9 years ago

haf commented 9 years ago

Is it possible to extend the serialisation functions from another assembly? I'm trying to, but it seems the compiler isn't finding the static methods/functions.

kolektiv commented 9 years ago

This, sadly, is a limitation of the static type inference mechanism. Extending types in other assemblies doesn't work (here or in other libs approaches). It's annoying! I tend to find it's not a huge pain and I either provide the serialisation in my libs and don't worry about it, or occasionally end up with, essentially, serialisable DTOs (which always feels a bit superfluous).

It's an annoyance, and I'd love to come up with a way round it, but short of some fairly major compiler changes I don't think it's possible.

haf commented 9 years ago

I'm having trouble even type-extending in the same assembly; from a different module... The thing is that I need to perform a lookup, so I can't put the static method on the actual type -- the module that handles the lookup is further down. screen shot 2015-04-20 at 23 20 23

kolektiv commented 9 years ago

Hmmm. Strange, the type inference does cause some issues in some cases. I'm not sure how much of a compiler change it would need to fix! I remember getting a good explanation of the limitations from Mauricio Scheffer when discussing Fleece, but can't find it now...

On 20 April 2015 at 22:20, Henrik Feldt notifications@github.com wrote:

I'm having trouble even type-extending in the same assembly; from a different module... The thing is that I need to perform a lookup, so I can't put the static method on the actual type -- the module that handles the lookup is further down. [image: screen shot 2015-04-20 at 23 20 23] https://cloud.githubusercontent.com/assets/193115/7240769/d5d0ee48-e7b3-11e4-9130-845c1cdc8bf3.png

— Reply to this email directly or view it on GitHub https://github.com/xyncro/chiron/issues/16#issuecomment-94571950.

kolektiv commented 9 years ago

Playing with this, yes, it's a bit of a limitation of static type inference. You might be able to work around it in some cases though where you have to extend the types outside of a valid scope. This is ugly, but might provide a pointer to something nicer...

open Aether
open Aether.Operators
open Chiron
open Chiron.Operators

let toJson f =
    snd (f (Json.Object Map.empty))

module Types =

    type One =
        { Value: string }

    type Two =
        { Prop: string
          One: One }

module Extend =

    open Types

    let lens =
        idLens <-?> Json.ObjectPIso >??> mapPLens "one"

    type One with

        static member ToJson (x: One) =
                Json.write "value" x.Value

    type Two with

        static member ToJson (x: Two) =
                Json.write "prop" x.Prop
             *> Json.setLensPartial lens (toJson (One.ToJson x.One))

open Types
open Extend

[<EntryPoint>]
let main _ =

    let two =
        { Prop = "Hello"
          One = { Value = "World" } }

    let json = toJson (Two.ToJson two)

    0
haf commented 9 years ago

For future visitors: so my conclusion is that; for type extensions to be compiled into the same type, you can't have them in different modules -- which -- taken together with the module-only-once restriction of F# -- means you probably have to collapse a lot of your code into the same module, rather than splitting it up by files. By putting the type extensions next to the types themselves, they get compiled into the proper/real type by the compiler, and consumers of the lib can use them.