Skip to content

Instantly share code, notes, and snippets.

@atsapura
Last active January 4, 2025 05:27
Show Gist options
  • Select an option

  • Save atsapura/fd9d7aa26e337eaa2f7f04d6cbb58ef6 to your computer and use it in GitHub Desktop.

Select an option

Save atsapura/fd9d7aa26e337eaa2f7f04d6cbb58ef6 to your computer and use it in GitHub Desktop.

Revisions

  1. atsapura revised this gist Jun 6, 2019. 1 changed file with 5 additions and 1 deletion.
    6 changes: 5 additions & 1 deletion Srtp and Records.fs
    Original file line number Diff line number Diff line change
    @@ -35,12 +35,16 @@ Active patterns are not required for this, but they do make code much easier to
    let inline printNameAndId (HasName n & HasEntityId id) = sprintf "name %s id %s" (n()) (id())

    // Watching it in action:
    let test1 = { EntityId = "123" }
    let test2 = {Test2.EntityId = "123"; Name = "123"; Length = 2}

    // Works both with Test and Test2 types since both have `EntityId` field
    let a = entityId { EntityId = "123" }
    let a = entityId test1
    let b = entityId test2

    let c = name test2

    let d = printNameAndId test2

    // *Compile* error when using type without required fields:
    let error = printNameAndId test1
  2. atsapura revised this gist Jun 6, 2019. 1 changed file with 25 additions and 3 deletions.
    28 changes: 25 additions & 3 deletions Srtp and Records.fs
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,13 @@

    (*
    WHAT'S GOING ON HERE?!
    Sometimes you don't care about a particular type, you're interested in one field only, let's say `EntityId`.
    Instead of using interface (which isn't even possible if don't own a type),
    we can do structural typing in F# using SRTP and Active Patterns.
    Active patterns are not required for this, but they do make code much easier to use.
    *)
    // So we have 2 types with field `EntityId: string`:
    type Test =
    { EntityId: string }

    @@ -7,18 +16,31 @@
    Name: string
    Length: int }

    // First we define active pattern for detecting
    // whether this type has this field or not:
    let inline (|HasEntityId|) x =
    fun () -> (^a : (member EntityId: string) x)
    fun () -> (^a : (member EntityId: string) x) // as you can see, SRTP syntax is hardly comfortable to use

    // Then we define function for retrieving this field
    let inline entityId (HasEntityId f) = f()

    // Another AP, this time for detecting `Name: string`:
    let inline (|HasName|) n =
    fun () -> (^a : (member Name: string) n)

    // and function for getting the name:
    let inline name (HasName f) = f()
    let inline test2f (HasName n & HasEntityId id) = sprintf "name %s id %s" (n()) (id())

    // Now here's the beauty of it: we can combine them!
    let inline printNameAndId (HasName n & HasEntityId id) = sprintf "name %s id %s" (n()) (id())

    // Watching it in action:
    let test2 = {Test2.EntityId = "123"; Name = "123"; Length = 2}

    // Works both with Test and Test2 types since both have `EntityId` field
    let a = entityId { EntityId = "123" }
    let b = entityId test2

    let c = name test2
    let d = test2f test2

    let d = printNameAndId test2
  3. atsapura created this gist Jun 3, 2019.
    24 changes: 24 additions & 0 deletions Srtp and Records.fs
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,24 @@

    type Test =
    { EntityId: string }

    type Test2 =
    { EntityId: string
    Name: string
    Length: int }

    let inline (|HasEntityId|) x =
    fun () -> (^a : (member EntityId: string) x)

    let inline entityId (HasEntityId f) = f()

    let inline (|HasName|) n =
    fun () -> (^a : (member Name: string) n)

    let inline name (HasName f) = f()
    let inline test2f (HasName n & HasEntityId id) = sprintf "name %s id %s" (n()) (id())
    let test2 = {Test2.EntityId = "123"; Name = "123"; Length = 2}
    let a = entityId { EntityId = "123" }
    let b = entityId test2
    let c = name test2
    let d = test2f test2