Skip to content

Instantly share code, notes, and snippets.

@akhansari
Forked from swlaschin/effective-fsharp.md
Created October 28, 2019 17:11
Show Gist options
  • Select an option

  • Save akhansari/3189c8350061520d9eab132c602bb33a to your computer and use it in GitHub Desktop.

Select an option

Save akhansari/3189c8350061520d9eab132c602bb33a to your computer and use it in GitHub Desktop.

Revisions

  1. @swlaschin swlaschin revised this gist Aug 26, 2019. 1 changed file with 84 additions and 42 deletions.
    126 changes: 84 additions & 42 deletions effective-fsharp.md
    Original file line number Diff line number Diff line change
    @@ -20,7 +20,7 @@ It's good to switch between top-down (decomposition) and bottom up coding.
    * Top down: Decide on the inputs and outputs first, then implement based on that.
    Then break that down into smaller functions than can be composed in a pipeline.
    * Bottom up: write functions the way they *should* be, as independent things,
    then fit them together use "adapters" such as `bind`, `map` etc
    then fit them together using "adapters" such as `bind`, `map` etc
    * To parameterise behavior, pass in a function!

    Grouping code:
    @@ -30,7 +30,7 @@ Grouping code:
    Make illegal states unrepresentable:
    * use state machines to avoid using `option` for data which is not used in a particular state
    * avoid bools
    * when values change together, group then together (e.g `a option and b option` becomes `(a*b) option`)
    * when values change together, group them together (e.g `a option and b option` becomes `(a*b) option`)


    ## Setting up a project
    @@ -43,7 +43,7 @@ Make illegal states unrepresentable:
    * A "module" is like a C# class with only static methods

    If defining types only (eg domain), use a namespace.
    If defining functions, they *MUST* be in a module (otherwise youe get a compiler error)
    If defining functions, they *MUST* be in a module (otherwise you get a compiler error)

    For functions closely associated with a type (eg `create`/`value`), use a module with the same name as the type.

    @@ -54,7 +54,7 @@ NOTES:

    TIP: Open few modules:
    * Use qualified names so that readers know what you are referencing. Don't force people to remember things!
    Eg `List.map` rather than `open List` and then using `map` later in the file.
    Eg `List.map` rather than `open List` and then using unqualified `map` later in the file.
    * You can force this with the `[<RequireQualifiedAccess>]` attribute on a module!
    * Also, open modules as close as possible to where they are used (e.g. in a submodule rather than the top of the file)

    @@ -120,8 +120,8 @@ This maps nicely to the Onion architecture
    Example module structure:

    1. Common libraries
    1. domain types
    1. workflows
    1. Domain types
    1. Workflows/use-cases
    1. DTOs
    1. DB/Infrastructure
    1. API interfaces
    @@ -139,19 +139,19 @@ For example: https://github.com/swlaschin/DomainModelingMadeFunctional/blob/mast

    If you want to swap out or hide infrastructure, you can use multiple assembles. For example:

    MyDomain.Core assembly
    `MyDomain.Core` assembly
    * Common lib
    * Domain types
    * Workflows (with infrastructure dependencies injected in later)
    * API

    MyDomain.Azure assembly
    `MyDomain.Azure` assembly
    * DB/Infrastructure for Azure

    MyDomain.AWS assembly
    `MyDomain.AWS` assembly
    * DB/Infrastructure for AWS

    MyDomain.Web assembly (top-level assembly)
    `MyDomain.Web` assembly (top-level assembly)
    * Composition Root that combines everything and exposes the API


    @@ -182,10 +182,16 @@ let xxxx xx xx =

    ### Function style

    Two styles that I use:

    * "stanza" style. Code is divided into related groups like stanzas in poetry.
    * "recipe" style. Helper functions are defined (the "ingredients") and then combined in a pipeline.

    Here's a "stanza" style function:

    ```fsharp
    // "stanza" style
    let xxx =
    let myFunction xxx =
    // part 1
    do this
    andthen this
    @@ -199,11 +205,13 @@ let xxx =
    ...
    ```

    A long function is ok if everything belongs together -- don't split it up for the sake of it
    I believe a long function is perfectly fine if everything belongs together -- don't split it up for the sake of it

    Here's a "recipe" style function:

    ```fsharp
    // "recipe" style
    let xxx =
    let myFunction xxx =
    // assemble ingredients
    let helperA = ....
    let helperB = ...
    @@ -216,7 +224,7 @@ let xxx =
    |> helperC
    ```

    If you have "big recipe" when ingredients are more than one liners, then create a private helper module to put the code in.
    If you have a "big recipe" when the "ingredients" are more than one liners, then consider creating a private helper module to put the "ingredients" in.

    ```fsharp
    module private Ingredients =
    @@ -232,7 +240,7 @@ module private Ingredients =
    open Ingredients
    // public
    let myPublicFn =
    let myFunction xxx xxx =
    // combine the ingredients defined above
    start
    |> helperA
    @@ -286,25 +294,27 @@ type Payment =

    ### Formatting match expressions

    Line up the vertical bars under the `match`:

    ```fsharp
    let xxx p =
    match p with
    | CaseA z -> ...
    | CaseB z -> ...
    | CaseC z -> ...
    | CaseA z -> handler...
    | CaseB z -> handler...
    | CaseC z -> handler...
    ```

    For longer ones, put each handler in a new block
    When the "handlers" for each case become longer than a one-liner, put each handler in a new block underneath:

    ```fsharp
    let xxx p =
    match p with
    | CaseA z ->
    ...
    handler...
    | CaseB z ->
    ...
    handler...
    | CaseD z ->
    ...
    handler...
    ```

    ### Formatting inline lambdas
    @@ -331,7 +341,7 @@ change, breaking the "diff" rule above.

    ```fsharp
    // example of BAD indenting.
    // If List.map is changed to List.choose, say, the entire block changes.
    // If List.map is changed to List.choose, say, the entire block changes which messes up the diff
    aCollection
    |> List.map (fun x ->
    xxxxx xxxx
    @@ -350,7 +360,7 @@ For all others, use C# style form (e.g. `Result<T,U>`).

    ### Use exhaustive pattern matching
    * Always be explicit and match every pattern
    * Avoid using `_` as a wildcard pattern
    * Avoid using `_` as a wildcard pattern. Exception needed for matching strings and ints, where you often have to.
    * If you do this, the compiler will warn you when new cases are added.
    If you don't do this, you will never know!

    @@ -359,7 +369,7 @@ For all others, use C# style form (e.g. `Result<T,U>`).

    Often there are two similar bits of code but slightly different. In this case, it's easy to ignore subtle differences.

    FIX: parameterize with a function parameter! This forces differences to be made explicit
    FIX: parameterize with a function parameter! This forces differences to be made explicit:

    * Example 1: iterating (aka fold) https://youtu.be/E8I19uA-wGY?t=1100
    * Example 2: continuations https://youtu.be/E8I19uA-wGY?t=1966
    @@ -373,7 +383,7 @@ But don't use `Result` for everything!
    Throw an exception and catch at a top-level function.
    * Exceptions can be better than `Result` if the scope is clear and very local (e.g. drilling down in a tree and throwing to exit in the middle of iteration)

    If you DO throw exceptions as part the API, put that in the signature:
    If you DO throw exceptions as part of the API, make it clear in the name of the function:

    ```fsharp
    List.tryHead // ok
    @@ -403,7 +413,8 @@ Purity is one goal but it's not the only goal.

    ### Operators

    Avoid using operators from other modules. Instead define them at the top of module they're used in.
    Avoid importing operators from other modules. It will be hard for a reader to know where they came from.
    Instead define operators at the top of module they're used in (or even just in the function they are used in).

    ```fsharp
    module xxxxx
    @@ -436,7 +447,7 @@ Documentation on doing OO in F# is here: https://fsharpforfunandprofit.com/serie

    ```
    module DictionaryExample =
    // needed!
    // needed to reference IDictionary
    open System.Collections.Generic
    // create a dictionary from a list of pairs
    @@ -448,7 +459,7 @@ module DictionaryExample =
    | true, value -> Some value
    | false,_ -> None
    // this style is used when piping the dictionary in
    // this style is used when piping the dictionary into a key check
    myDict |> tryGetValue 1
    myDict |> tryGetValue 42
    @@ -460,15 +471,17 @@ module DictionaryExample =
    | false,_ -> None
    // this style is used when you want to bake in the dictionary
    // in a helper function
    // in a helper function and then pass the keys in later
    let lookup = tryGetValue2 myDict
    lookup 1
    lookup 42
    ```

    ## Working with the Result type

    If using `Result`, include this [Result.fs](https://github.com/swlaschin/DomainModelingMadeFunctional/blob/master/src/OrderTaking/Result.fs) file at the top of your project. (OK to duplicate in many projects to avoid dependencies)
    If using `Result`, include this [Result.fs](https://github.com/swlaschin/DomainModelingMadeFunctional/blob/master/src/OrderTaking/Result.fs) file at the top of your project.

    I am fine with duplicating this file in many projects to avoid dependencies on nuget or library DLLs.

    ### To chain results in series ("monads")

    @@ -505,27 +518,56 @@ let finalResult = result {

    Use the `Validation` type in `Result.fs`. It's the same type as `Result` but with a *list* of errors.

    If using validation (multiple errors), the validation code has the same pattern
    If need to do validation (multiple errors), the validation code has the same pattern
    * Define a ctor for the type
    * Create all the values (which return `Results`)
    * Use the applicative style to construct the object.
    * Option 1: Use the applicative style to construct the object.
    * First param has `<!>` in front
    * Subsequent params have `<*>` in front
    * (optional) Map the list of validation errors to an error case so you have a normal `Result` again
    * Option 2: Lift the `ctor` using `lift2`, `lift3`, lift4` etc., depending on how many parameters the ctor has.
    * When validation is complete and you are returning to normal code, consider mapping the list of validation errors to an error case so you have a normal `Result` again.

    ```fsharp
    // define at top
    // Option 1: Applicative style
    // define at top of file
    let ( <!> ) = Validation.map
    let ( <*> ) = Validation.apply
    let ctor = ...
    let firstParam = ... a Result
    let secondParam = ... a Result
    let thirdParam = ... a Result
    let myObjectOrResult = ctor <!> firstParam <*> secondParam <*> thirdParam
    myObjectOrResult |> Result.mapError ValidationError
    type MyError =
    | ValidationError of string list
    | DbError of string
    | etc
    let createMyObject param1 param2 param3 =
    { Field1 = param1; etc ... }
    let param1OrError = ...validate and return a Result
    let param2OrError = ...validate and return a Result
    let param3OrError = ...validate and return a Result
    let myObjectOrError = createMyObject <!> param1OrError <*> param2OrError <*> param3OrError
    // convert to a Result<_,MyError> without a list of errors
    myObjectOrError |> Result.mapError ValidationError
    ```

    ```fsharp
    // Option 2: Using lift
    type MyError = ...
    let createMyObject param1 param2 param3 =
    { Field1 = param1; etc ... }
    let param1OrError = ...validate and return a Result
    let param2OrError = ...validate and return a Result
    let param3OrError = ...validate and return a Result
    let myObjectOrError = (Validation.lift3 createMyObject) param1OrError param2OrError param3OrError
    // convert to a Result<_,MyError> without a list of errors
    myObjectOrError |> Result.mapError ValidationError
    ```


    See also:
    * https://github.com/swlaschin/Railway-Oriented-Programming-Example/blob/master/src/FsRopExample/Dtos.fs#L75

    @@ -571,7 +613,7 @@ Define them in their own module.
    For each DTO, define an associated module with functions "toDomain" and "fromDomain"

    ```fsharp
    namespace CurrentAwareness.Dto
    namespace MyDomain.Dto
    type MyDto = {
    Something: string
  2. @swlaschin swlaschin revised this gist Apr 26, 2019. No changes.
  3. @swlaschin swlaschin revised this gist Apr 24, 2019. 1 changed file with 2 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions effective-fsharp.md
    Original file line number Diff line number Diff line change
    @@ -430,6 +430,8 @@ See https://fsharpforfunandprofit.com/posts/type-extensions/#methods-dont-play-w
    Similarly, it's OK to use interfaces to define groups of functions that can have multiple implementations.
    See https://fsharpforfunandprofit.com/posts/interfaces/

    Documentation on doing OO in F# is here: https://fsharpforfunandprofit.com/series/object-oriented-programming-in-fsharp.html

    ## Working with Dictionaries

    ```
  4. @swlaschin swlaschin revised this gist Apr 24, 2019. 1 changed file with 4 additions and 4 deletions.
    8 changes: 4 additions & 4 deletions effective-fsharp.md
    Original file line number Diff line number Diff line change
    @@ -532,12 +532,12 @@ See also:
    To validate a list, validate each item using `List.map` and then use `Result.sequence` to put the `Result` type on the outside.

    ```fsharp
    let topicsOrError =
    dto.Topics
    let itemsOrError =
    itemDtos
    // convert to F# list
    |> List.ofArray
    // convert each item
    |> List.map (PracticeAreaTopicDto.toDomain)
    // convert each DTO item to a domain object
    |> List.map (ItemDto.toDomain)
    // flip from List<Result<>> to Result<List<>>
    |> Result.sequence
    ```
  5. @swlaschin swlaschin revised this gist Apr 24, 2019. 1 changed file with 15 additions and 0 deletions.
    15 changes: 15 additions & 0 deletions effective-fsharp.md
    Original file line number Diff line number Diff line change
    @@ -527,6 +527,21 @@ myObjectOrResult |> Result.mapError ValidationError
    See also:
    * https://github.com/swlaschin/Railway-Oriented-Programming-Example/blob/master/src/FsRopExample/Dtos.fs#L75

    ### Validating lists of items

    To validate a list, validate each item using `List.map` and then use `Result.sequence` to put the `Result` type on the outside.

    ```fsharp
    let topicsOrError =
    dto.Topics
    // convert to F# list
    |> List.ofArray
    // convert each item
    |> List.map (PracticeAreaTopicDto.toDomain)
    // flip from List<Result<>> to Result<List<>>
    |> Result.sequence
    ```


    ## Constrained types

  6. @swlaschin swlaschin revised this gist Apr 24, 2019. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion effective-fsharp.md
    Original file line number Diff line number Diff line change
    @@ -1,7 +1,7 @@
    ## Architecture

    * Use Onion architecture
    * Dependencies go inwards * That is, the Core domain doesn't know about outside layers
    * Dependencies go inwards. That is, the Core domain doesn't know about outside layers

    * Use pipeline model to implement workflows/use-cases/stories
    * Business logic makes decisions
  7. @swlaschin swlaschin revised this gist Apr 24, 2019. 1 changed file with 4 additions and 3 deletions.
    7 changes: 4 additions & 3 deletions effective-fsharp.md
    Original file line number Diff line number Diff line change
    @@ -1,8 +1,7 @@
    ## Architecture

    * Use Onion architecture
    * Dependencies go inwards
    * That is, the Core domain doesn't know about outside layers
    * Dependencies go inwards * That is, the Core domain doesn't know about outside layers

    * Use pipeline model to implement workflows/use-cases/stories
    * Business logic makes decisions
    @@ -487,7 +486,9 @@ myResult
    |> Result.bind pointsFunctionB
    ```

    You can use `result` computation expressions instead:
    ## Result computation expressions instead of bind

    You can use `result` computation expressions instead of `Result.bind`:

    ```fsharp
    let finalResult = result {
  8. @swlaschin swlaschin revised this gist Apr 24, 2019. 1 changed file with 54 additions and 15 deletions.
    69 changes: 54 additions & 15 deletions effective-fsharp.md
    Original file line number Diff line number Diff line change
    @@ -340,7 +340,7 @@ aCollection
    )
    ```

    ### Generic types
    ### Formatting Generic types

    F# allows generic types to be used as a suffix. Eg `'a list` or `List<'a>`.

    @@ -431,6 +431,40 @@ See https://fsharpforfunandprofit.com/posts/type-extensions/#methods-dont-play-w
    Similarly, it's OK to use interfaces to define groups of functions that can have multiple implementations.
    See https://fsharpforfunandprofit.com/posts/interfaces/

    ## Working with Dictionaries

    ```
    module DictionaryExample =
    // needed!
    open System.Collections.Generic
    // create a dictionary from a list of pairs
    let myDict = [ (1,"a"); (2,"b") ] |> dict
    /// get a value from a dictionary, with dictionary as LAST parameter
    let tryGetValue key (dict:IDictionary<_,_>) =
    match dict.TryGetValue(key) with
    | true, value -> Some value
    | false,_ -> None
    // this style is used when piping the dictionary in
    myDict |> tryGetValue 1
    myDict |> tryGetValue 42
    /// get a value from a dictionary, with dictionary as FIRST parameter
    let tryGetValue2 (dict:IDictionary<_,_>) key =
    match dict.TryGetValue(key) with
    | true, value -> Some value
    | false,_ -> None
    // this style is used when you want to bake in the dictionary
    // in a helper function
    let lookup = tryGetValue2 myDict
    lookup 1
    lookup 42
    ```

    ## Working with the Result type

    If using `Result`, include this [Result.fs](https://github.com/swlaschin/DomainModelingMadeFunctional/blob/master/src/OrderTaking/Result.fs) file at the top of your project. (OK to duplicate in many projects to avoid dependencies)
    @@ -466,22 +500,27 @@ let finalResult = result {

    ### To combine results in parallel ("applicatives")

    Use the `Validation` type in `Result.fs`. It's the same as `Result` but with a *list* of errors.
    Use the `Validation` type in `Result.fs`. It's the same type as `Result` but with a *list* of errors.

    If using validation (multiple errors), the validation code has the same pattern
    * Define a ctor for the DTO
    * Define a ctor for the type
    * Create all the values (which return `Results`)
    * Use the applicative style to construct the object
    * Map the list of validation errors to an error case
    * Use the applicative style to construct the object.
    * First param has `<!>` in front
    * Subsequent params have `<*>` in front
    * (optional) Map the list of validation errors to an error case so you have a normal `Result` again

    ```fsharp
    let toDomainObj dto =
    let ctor = ...
    let firstParam = ... dto.First
    let secondParam = ...
    let thirdParam = ...
    let domainObjR = ctor <!> firstParam <*> secondParam <*> thirdParam
    domainObjR |> Result.mapError ValidationError
    // define at top
    let ( <!> ) = Validation.map
    let ( <*> ) = Validation.apply
    let ctor = ...
    let firstParam = ... a Result
    let secondParam = ... a Result
    let thirdParam = ... a Result
    let myObjectOrResult = ctor <!> firstParam <*> secondParam <*> thirdParam
    myObjectOrResult |> Result.mapError ValidationError
    ```
    See also:
    @@ -530,7 +569,7 @@ module MyDto =
    let fromDomain (domainObj:MyDomain) :MyDto =
    ```

    If using validation (multiple errors), the `toDomain` code has the same pattern
    If using validation (multiple errors), the `toDomain` code has the "applicative" pattern described above.
    * Define a ctor for the DTO
    * Create all the values (which return Results)
    * Use the applicative style to construct the DTO
    @@ -539,8 +578,8 @@ If using validation (multiple errors), the `toDomain` code has the same pattern
    ```fsharp
    let toDomainObj dto =
    let ctor = ...
    let firstParam = ... dto.First
    let secondParam = ...
    let firstParam = ... construct from dto.First
    let secondParam = ... construct from dto.Second
    let thirdParam = ...
    let domainObjR = ctor <!> firstParam <*> secondParam <*> thirdParam
    domainObjR |> Result.mapError ValidationError
  9. @swlaschin swlaschin revised this gist Apr 24, 2019. 1 changed file with 56 additions and 0 deletions.
    56 changes: 56 additions & 0 deletions effective-fsharp.md
    Original file line number Diff line number Diff line change
    @@ -431,6 +431,62 @@ See https://fsharpforfunandprofit.com/posts/type-extensions/#methods-dont-play-w
    Similarly, it's OK to use interfaces to define groups of functions that can have multiple implementations.
    See https://fsharpforfunandprofit.com/posts/interfaces/

    ## Working with the Result type

    If using `Result`, include this [Result.fs](https://github.com/swlaschin/DomainModelingMadeFunctional/blob/master/src/OrderTaking/Result.fs) file at the top of your project. (OK to duplicate in many projects to avoid dependencies)

    ### To chain results in series ("monads")

    If the first value is not a `Result`:

    ```fsharp
    myValue
    |> pointsFunctionA
    |> Result.bind pointsFunctionB
    ```

    If the first value *is* a `Result`:

    ```fsharp
    myResult
    |> Result.bind pointsFunctionA
    |> Result.bind pointsFunctionB
    ```

    You can use `result` computation expressions instead:

    ```fsharp
    let finalResult = result {
    let! x = myResult
    let! y = pointsFunctionA x
    let! z = pointsFunctionB y
    return z
    }
    ```

    ### To combine results in parallel ("applicatives")

    Use the `Validation` type in `Result.fs`. It's the same as `Result` but with a *list* of errors.

    If using validation (multiple errors), the validation code has the same pattern
    * Define a ctor for the DTO
    * Create all the values (which return `Results`)
    * Use the applicative style to construct the object
    * Map the list of validation errors to an error case

    ```fsharp
    let toDomainObj dto =
    let ctor = ...
    let firstParam = ... dto.First
    let secondParam = ...
    let thirdParam = ...
    let domainObjR = ctor <!> firstParam <*> secondParam <*> thirdParam
    domainObjR |> Result.mapError ValidationError
    ```
    See also:
    * https://github.com/swlaschin/Railway-Oriented-Programming-Example/blob/master/src/FsRopExample/Dtos.fs#L75


    ## Constrained types

  10. @swlaschin swlaschin revised this gist Apr 23, 2019. 1 changed file with 6 additions and 0 deletions.
    6 changes: 6 additions & 0 deletions effective-fsharp.md
    Original file line number Diff line number Diff line change
    @@ -567,6 +567,12 @@ module Result =
    ```


    ## Common compiler errors

    * Inconsistent type. Fix: add type annotations to find where the error is.
    * Value restriction. Fix: add a parameter.
    * See also https://fsharpforfunandprofit.com/troubleshooting-fsharp/


    ## Common errors in F# Interactive

  11. @swlaschin swlaschin revised this gist Apr 23, 2019. 1 changed file with 20 additions and 0 deletions.
    20 changes: 20 additions & 0 deletions effective-fsharp.md
    Original file line number Diff line number Diff line change
    @@ -432,6 +432,26 @@ Similarly, it's OK to use interfaces to define groups of functions that can have
    See https://fsharpforfunandprofit.com/posts/interfaces/


    ## Constrained types

    Define a type with a private constructor and then a helper module (in same scope) with same name. Helper module should have `create` and `value` functions

    ```fsharp
    type EmailAddress = private EmailAddress of string
    module EmailAddress =
    let create str :Result<EmailAddress,_> = // or Option
    if String.IsNullOrEmpty(str) then
    Error ...
    else if (* check validity *) then
    Ok (EmailAddress str)
    else
    Error ...
    let value (EmailAddress str) = str
    ```


    ## DTOs and Validation

    Define them in their own module.
  12. @swlaschin swlaschin revised this gist Apr 23, 2019. 1 changed file with 80 additions and 5 deletions.
    85 changes: 80 additions & 5 deletions effective-fsharp.md
    Original file line number Diff line number Diff line change
    @@ -14,7 +14,7 @@
    * Only need to unit test the business logic
    * IO is tested as part of integration tests

    ## Design
    ## High-level design

    It's good to switch between top-down (decomposition) and bottom up coding.

    @@ -28,6 +28,15 @@ Grouping code:
    * Types that change together should live together (e.g. in same module)
    * Functions that change together should live together (e.g. in same module)

    Make illegal states unrepresentable:
    * use state machines to avoid using `option` for data which is not used in a particular state
    * avoid bools
    * when values change together, group then together (e.g `a option and b option` becomes `(a*b) option`)


    ## Setting up a project

    * Compiler settings: warn unused "-warnon:1182"

    ## Modules and namespaces

    @@ -44,6 +53,13 @@ NOTES:
    * You can use "rec" for recursive modules so that the definitions don't have to be in order.


    TIP: Open few modules:
    * Use qualified names so that readers know what you are referencing. Don't force people to remember things!
    Eg `List.map` rather than `open List` and then using `map` later in the file.
    * You can force this with the `[<RequireQualifiedAccess>]` attribute on a module!
    * Also, open modules as close as possible to where they are used (e.g. in a submodule rather than the top of the file)


    ## Signature files

    * have `fsi` file extension.
    @@ -324,7 +340,69 @@ aCollection
    )
    ```

    ## Operators
    ### Generic types

    F# allows generic types to be used as a suffix. Eg `'a list` or `List<'a>`.

    Which one to use? Answer: Use suffix form for built-in types such as `int list`, `int seq`, `int option`, `int[]`.
    For all others, use C# style form (e.g. `Result<T,U>`).

    ## Coding tips

    ### Use exhaustive pattern matching
    * Always be explicit and match every pattern
    * Avoid using `_` as a wildcard pattern
    * If you do this, the compiler will warn you when new cases are added.
    If you don't do this, you will never know!


    ### Avoid boilerplate

    Often there are two similar bits of code but slightly different. In this case, it's easy to ignore subtle differences.

    FIX: parameterize with a function parameter! This forces differences to be made explicit

    * Example 1: iterating (aka fold) https://youtu.be/E8I19uA-wGY?t=1100
    * Example 2: continuations https://youtu.be/E8I19uA-wGY?t=1966

    ### Make common errors obvious

    Return a `Result` or `Option` rather than an exception.

    But don't use `Result` for everything!
    * There is a difference between "domain errors" and "panics". Panics do not need to be exposed in the domain.
    Throw an exception and catch at a top-level function.
    * Exceptions can be better than `Result` if the scope is clear and very local (e.g. drilling down in a tree and throwing to exit in the middle of iteration)

    If you DO throw exceptions as part the API, put that in the signature:

    ```fsharp
    List.tryHead // ok
    List.headExn // ok
    List.head // bad (sadly this is how the standard library does it
    ```

    Similarly

    ```fsharp
    List.find // bad unless you KNOW that the item exists
    List.tryFind // good
    ```

    ### Complex type hacks

    Don't do it!

    F# has Statically Resolved Type Parameters (SRTP) which can be used to do polymorphism, monads, etc. Don't!

    KISS - simple is better than complex!

    ### Mutability

    Mutable values are OK if they are local and no one sees them. But don't go overboard!
    Purity is one goal but it's not the only goal.

    ### Operators

    Avoid using operators from other modules. Instead define them at the top of module they're used in.

    @@ -469,9 +547,6 @@ module Result =
    ```


    ## Setting up

    * Compiler settings: warn unused "-warnon:1182"

    ## Common errors in F# Interactive

  13. @swlaschin swlaschin revised this gist Apr 23, 2019. 1 changed file with 3 additions and 0 deletions.
    3 changes: 3 additions & 0 deletions effective-fsharp.md
    Original file line number Diff line number Diff line change
    @@ -469,6 +469,9 @@ module Result =
    ```


    ## Setting up

    * Compiler settings: warn unused "-warnon:1182"

    ## Common errors in F# Interactive

  14. @swlaschin swlaschin revised this gist Apr 23, 2019. 1 changed file with 14 additions and 0 deletions.
    14 changes: 14 additions & 0 deletions effective-fsharp.md
    Original file line number Diff line number Diff line change
    @@ -14,6 +14,20 @@
    * Only need to unit test the business logic
    * IO is tested as part of integration tests

    ## Design

    It's good to switch between top-down (decomposition) and bottom up coding.

    * Top down: Decide on the inputs and outputs first, then implement based on that.
    Then break that down into smaller functions than can be composed in a pipeline.
    * Bottom up: write functions the way they *should* be, as independent things,
    then fit them together use "adapters" such as `bind`, `map` etc
    * To parameterise behavior, pass in a function!

    Grouping code:
    * Types that change together should live together (e.g. in same module)
    * Functions that change together should live together (e.g. in same module)


    ## Modules and namespaces

  15. @swlaschin swlaschin revised this gist Apr 23, 2019. 1 changed file with 21 additions and 0 deletions.
    21 changes: 21 additions & 0 deletions effective-fsharp.md
    Original file line number Diff line number Diff line change
    @@ -310,6 +310,20 @@ aCollection
    )
    ```

    ## Operators

    Avoid using operators from other modules. Instead define them at the top of module they're used in.

    ```fsharp
    module xxxxx
    // define at top
    let ( <!> ) = Validation.map
    let ( <*> ) = Validation.apply
    // use
    ctor <!> firstParam <*> secondParam
    ```


    ## OO code vs FP code
    @@ -442,5 +456,12 @@ module Result =



    ## Common errors in F# Interactive

    * Can't use namespaces in interactive
    * Need to explitly refer to other files using #load or #r
    * Type mismatch even though you know its the same!
    * caused by recompiling one bit without recompiling the other bit
    * FIX: recompile the whole thing


  16. @swlaschin swlaschin revised this gist Apr 23, 2019. 1 changed file with 3 additions and 3 deletions.
    6 changes: 3 additions & 3 deletions effective-fsharp.md
    Original file line number Diff line number Diff line change
    @@ -96,9 +96,9 @@ Example module structure:
    1. DTOs
    1. DB/Infrastructure
    1. API interfaces
    1. Composition Root that combines everything and exposed API. Can be in a different assembly.
    1. Composition Root that combines everything and exposes the API.

    You use dots in the filename to make the namespace clear:
    You can use dots in the filename to make the namespace clear:
    * `Common.PrimitiveTypes`
    * `Common.Types`
    * `MyWorkflow.Types`
    @@ -123,7 +123,7 @@ MyDomain.AWS assembly
    * DB/Infrastructure for AWS

    MyDomain.Web assembly (top-level assembly)
    * Composition Root that combines everything and exposes API
    * Composition Root that combines everything and exposes the API


    ## Code formatting
  17. @swlaschin swlaschin revised this gist Apr 23, 2019. 1 changed file with 54 additions and 3 deletions.
    57 changes: 54 additions & 3 deletions effective-fsharp.md
    Original file line number Diff line number Diff line change
    @@ -80,6 +80,52 @@ module MyEntity =
    ```

    ## Organising modules - general

    F# is order sensitive -- modules only know about modules above them.
    This maps nicely to the Onion architecture
    * core domain is at top
    * IO near bottom
    * Main app (composition root/Startup) is always last

    Example module structure:

    1. Common libraries
    1. domain types
    1. workflows
    1. DTOs
    1. DB/Infrastructure
    1. API interfaces
    1. Composition Root that combines everything and exposed API. Can be in a different assembly.

    You use dots in the filename to make the namespace clear:
    * `Common.PrimitiveTypes`
    * `Common.Types`
    * `MyWorkflow.Types`
    * `MyWorkflow.Impl`

    For example: https://github.com/swlaschin/DomainModelingMadeFunctional/blob/master/src/OrderTaking/OrderTaking.fsproj

    ### Working with multiple assemblies

    If you want to swap out or hide infrastructure, you can use multiple assembles. For example:

    MyDomain.Core assembly
    * Common lib
    * Domain types
    * Workflows (with infrastructure dependencies injected in later)
    * API

    MyDomain.Azure assembly
    * DB/Infrastructure for Azure

    MyDomain.AWS assembly
    * DB/Infrastructure for AWS

    MyDomain.Web assembly (top-level assembly)
    * Composition Root that combines everything and exposes API


    ## Code formatting

    **Reading** happens much more than writing, so make it readble first. Make the INTENT understandable
    @@ -113,8 +159,8 @@ let xxxx xx xx =
    let xxx =
    // part 1
    do this
    and then this
    and then something else
    andthen this
    andthen something else
    // part 2
    start a new thing
    @@ -153,15 +199,20 @@ module private Ingredients =
    let helperC =
    ...
    // bring module into scope
    open Ingredients
    // public
    let xxx =
    let myPublicFn =
    // combine the ingredients defined above
    start
    |> helperA
    |> helperB
    |> helperC
    ```

    If you're using signature files and only one workflow per file, the "private" module is the whole `fs` file, so this approach is not needed.

    ### Format to make diffs more readable

    Diffs are an important part of reading so make diffs easy to understand. Changing one thing should cause only one line to change!
  18. @swlaschin swlaschin revised this gist Apr 23, 2019. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions effective-fsharp.md
    Original file line number Diff line number Diff line change
    @@ -87,7 +87,7 @@ During a code review if reader and writer disagree, reader is always right!

    Make blocks obvious style - have only **one** indent per scope change.

    ```fsharp
    ```
    // good
    let xxxx xx xx =
    xxxx xx xx
    @@ -126,7 +126,7 @@ let xxx =

    A long function is ok if everything belongs together -- don't split it up for the sake of it

    ```
    ```fsharp
    // "recipe" style
    let xxx =
    // assemble ingredients
  19. @swlaschin swlaschin revised this gist Apr 23, 2019. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions effective-fsharp.md
    Original file line number Diff line number Diff line change
    @@ -1,8 +1,8 @@
    ## Architecture

    * Use Onion architecture
    * Dependencies go inwards
    * That is, the Core domain doesn't know about outside layers
    * Dependencies go inwards
    * That is, the Core domain doesn't know about outside layers

    * Use pipeline model to implement workflows/use-cases/stories
    * Business logic makes decisions
  20. @swlaschin swlaschin revised this gist Apr 23, 2019. 1 changed file with 88 additions and 71 deletions.
    159 changes: 88 additions & 71 deletions effective-fsharp.md
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,86 @@
    ## General code layout
    ## Architecture

    * Use Onion architecture
    * Dependencies go inwards
    * That is, the Core domain doesn't know about outside layers

    * Use pipeline model to implement workflows/use-cases/stories
    * Business logic makes decisions
    * IO does storage with minimal logic
    * Keep Business logic and IO separate
    * Keep IO at edges

    * Testing
    * Only need to unit test the business logic
    * IO is tested as part of integration tests


    ## Modules and namespaces

    * A namespace is just like C#
    * A "module" is like a C# class with only static methods

    If defining types only (eg domain), use a namespace.
    If defining functions, they *MUST* be in a module (otherwise youe get a compiler error)

    For functions closely associated with a type (eg `create`/`value`), use a module with the same name as the type.

    NOTES:
    * You can't use namespaces in F# interactive
    * You can use "rec" for recursive modules so that the definitions don't have to be in order.


    ## Signature files

    * have `fsi` file extension.
    * must be above the corresponding `fs` file.

    ```
    | | FSI | FS |
    | top of file | namespace/module | same as FSI |
    | types | public | public types must be defined the same |
    | | | private types OK |
    | functions | use "val" | use "let" |
    ```

    Example signature file

    ```fsharp
    /// Signature file
    module MyDomain.Dto.Converter
    open MyDomain
    type DtoError =
    | BadName
    module MyEntity =
    val fromDomain : MyEntity -> MyEntityDto
    val toDomain : MyEntityDto -> Result<MyEntity,DtoError>
    ```

    Example implementation file

    ```fsharp
    module MyDomain.Dto.Converter
    open MyDomain
    type DtoError =
    | BadName
    type MyPrivateType = string
    module MyEntity =
    let fromDomain (x:MyEntity) :MyEntityDto =
    failwith "not implemented"
    let toDomain (x:MyEntityDto) :Result<MyEntity,DtoError> =
    failwith "not implemented"
    ```

    ## Code formatting

    **Reading** happens much more than writing, so make it readble first. Make the INTENT understandable
    During a code review if reader and writer disagree, reader is always right!
    @@ -80,7 +162,7 @@ let xxx =
    |> helperC
    ```

    ## Layout for diffs
    ### Format to make diffs more readable

    Diffs are an important part of reading so make diffs easy to understand. Changing one thing should cause only one line to change!

    @@ -179,84 +261,19 @@ aCollection



    ## OO style vs FP style
    ## OO code vs FP code

    It's OK to use OO style code if *behavior* is the most important thing -- that is, you want polymorphism.

    e.g. in `x.ToString()` we don't care what x is!
    See https://fsharpforfunandprofit.com/posts/type-extensions/
    e.g. in `x.ToString()` we don't care what x is.
    To define methods see https://fsharpforfunandprofit.com/posts/type-extensions/

    But this can mess with type inference :( You might well need to use type annotations more often.
    See https://fsharpforfunandprofit.com/posts/type-extensions/#methods-dont-play-well-with-type-inference

    Similarly, it's OK to use interfaces to define groups of functions that can have multiple implementations.
    See https://fsharpforfunandprofit.com/posts/interfaces/

    ## Modules and namespaces

    * A namespace is just like C#
    * A "module" is like a C# class with only static methods

    If defining types only (eg domain), use a namespace.
    If defining functions, they *MUST* be in a module (otherwise youe get a compiler error)

    For functions closely associated with a type (eg `create`/`value`), use a module with the same name as the type.

    NOTES:
    * You can't use namespaces in F# interactive
    * You can use "rec" for recursive modules so that the definitions don't have to be in order.


    ## Signature files

    * have `fsi` file extension.
    * must be above the corresponding `fs` file.

    ```
    | | FSI | FS |
    | top of file | namespace/module | same as FSI |
    | types | public | public types must be defined the same |
    | | | private types OK |
    | functions | use "val" | use "let" |
    ```

    Example signature file

    ```fsharp
    /// Signature file
    module CurrentAwareness.Dto.Converter
    open CurrentAwareness
    type DtoError =
    | BadName
    module CuratedItem =
    val fromDomain : CuratedItem -> CuratedItemDto
    val toDomain : CuratedItemDto -> Result<CuratedItem,DtoError>
    ```

    Example implementation file

    ```fsharp
    module CurrentAwareness.Dto.Converter
    open CurrentAwareness
    type DtoError =
    | BadName
    type MyPrivateType = string
    module CuratedItem =
    let fromDomain (x:CuratedItem) :CuratedItemDto =
    failwith "not implemented"
    let toDomain (x:CuratedItemDto) :Result<CuratedItem,DtoError> =
    failwith "not implemented"
    ```


    ## DTOs and Validation

  21. @swlaschin swlaschin revised this gist Apr 23, 2019. 1 changed file with 37 additions and 3 deletions.
    40 changes: 37 additions & 3 deletions effective-fsharp.md
    Original file line number Diff line number Diff line change
    @@ -14,7 +14,7 @@ let xxxx xx xx =
    xxxx xxx
    xxxx xx xxxx
    // bad blocks
    // badly indented blocks
    let xxxx xx xx =
    xxxx xx xx
    xxx xx
    @@ -144,15 +144,49 @@ let xxx p =
    | CaseD z ->
    ...
    ```


    ### Formatting inline lambdas

    Short ones can go on one line

    ```fsharp
    aCollection
    |> List.map (fun x -> x + 1)
    ```

    Longer ones may need to start a new block

    ```fsharp
    aCollection
    |> List.map (fun x ->
    xxxxx xxxx
    xxxx xxx
    )
    ```

    Do *NOT* have "hanging" lambdas. If the top line changes, the indentation of the entire block will
    change, breaking the "diff" rule above.

    ```fsharp
    // example of BAD indenting.
    // If List.map is changed to List.choose, say, the entire block changes.
    aCollection
    |> List.map (fun x ->
    xxxxx xxxx
    xxxx xxx
    )
    ```



    ## OO style vs FP style

    It's OK to use OO style code if *behavior* is the most important thing -- that is, you want polymorphism.

    e.g. in `x.ToString()` we don't care what x is!
    See https://fsharpforfunandprofit.com/posts/type-extensions/

    But this can mess with type inference :(
    But this can mess with type inference :( You might well need to use type annotations more often.

    Similarly, it's OK to use interfaces to define groups of functions that can have multiple implementations.
    See https://fsharpforfunandprofit.com/posts/interfaces/
  22. @swlaschin swlaschin revised this gist Apr 23, 2019. 1 changed file with 159 additions and 0 deletions.
    159 changes: 159 additions & 0 deletions effective-fsharp.md
    Original file line number Diff line number Diff line change
    @@ -1,3 +1,162 @@
    ## General code layout

    **Reading** happens much more than writing, so make it readble first. Make the INTENT understandable
    During a code review if reader and writer disagree, reader is always right!

    Make blocks obvious style - have only **one** indent per scope change.

    ```fsharp
    // good
    let xxxx xx xx =
    xxxx xx xx
    xxx xx
    xxxx xxx
    xxxx xxx
    xxxx xx xxxx
    // bad blocks
    let xxxx xx xx =
    xxxx xx xx
    xxx xx
    xxxx xxx
    xxxx xxx
    xxxx xx xxxx
    ```

    ### Function style


    ```fsharp
    // "stanza" style
    let xxx =
    // part 1
    do this
    and then this
    and then something else
    // part 2
    start a new thing
    and some more
    // part 3
    ...
    ```

    A long function is ok if everything belongs together -- don't split it up for the sake of it

    ```
    // "recipe" style
    let xxx =
    // assemble ingredients
    let helperA = ....
    let helperB = ...
    let helperC = ...
    // then combine the ingredients
    start
    |> helperA
    |> helperB
    |> helperC
    ```

    If you have "big recipe" when ingredients are more than one liners, then create a private helper module to put the code in.

    ```fsharp
    module private Ingredients =
    // define ingredients
    let helperA =
    ...
    let helperB =
    ...
    let helperC =
    ...
    // public
    let xxx =
    // combine the ingredients defined above
    start
    |> helperA
    |> helperB
    |> helperC
    ```

    ## Layout for diffs

    Diffs are an important part of reading so make diffs easy to understand. Changing one thing should cause only one line to change!

    ```fsharp
    // 1 or 2 fields
    type MyRecord = { fieldA:string; fieldB:int }
    // 3 or more fields
    type MyRecord = {
    fieldA : string
    fieldB : string
    fieldC : string
    fieldD : string
    }
    ```

    Use same style for constructing records

    ```fsharp
    let myRecord = {
    fieldA = "..."
    fieldB = "..."
    fieldC = "..."
    fieldD = "..."
    }
    ```

    ```fsharp
    // enum-style choices
    type Colour = Red | Blue | Green
    // single case choices
    type CustomerId = CustomerId of int
    // complex choices
    type Payment =
    | Cash
    | Check of CheckNumber
    | Card of CardInfo
    ```

    ### Formatting match expressions

    ```fsharp
    let xxx p =
    match p with
    | CaseA z -> ...
    | CaseB z -> ...
    | CaseC z -> ...
    ```

    For longer ones, put each handler in a new block

    ```fsharp
    let xxx p =
    match p with
    | CaseA z ->
    ...
    | CaseB z ->
    ...
    | CaseD z ->
    ...
    ```

    ## OO style vs FP style

    It's OK to use OO style code if *behavior* is the most important thing -- that is, you want polymorphism.

    e.g. in `x.ToString()` we don't care what x is!
    See https://fsharpforfunandprofit.com/posts/type-extensions/

    But this can mess with type inference :(

    Similarly, it's OK to use interfaces to define groups of functions that can have multiple implementations.
    See https://fsharpforfunandprofit.com/posts/interfaces/

    ## Modules and namespaces

    * A namespace is just like C#
  23. @swlaschin swlaschin revised this gist Apr 23, 2019. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion effective-fsharp.md
    Original file line number Diff line number Diff line change
    @@ -148,7 +148,7 @@ module Api =
    * F# `list` -- not available in C#
    * F# `int seq` -- same as `IEnumerable<int>` in C#
    * F# `ResizeArray<int>` -- same as `List<int>` in C#
    * F# arrays `int[]` -- same as `Array<int>` in C#
    * F# `int[]` -- same as `Array<int>` in C#


    * Have a CSharpHelper module with useful functions
  24. @swlaschin swlaschin revised this gist Apr 23, 2019. 1 changed file with 6 additions and 1 deletion.
    7 changes: 6 additions & 1 deletion effective-fsharp.md
    Original file line number Diff line number Diff line change
    @@ -144,7 +144,12 @@ module Api =
    let DoSomething x y x =
    ```

    * Use C# collections (seq or Array or ResizeArray) not F# list
    * Use C#-compatible collections not F# `list`
    * F# `list` -- not available in C#
    * F# `int seq` -- same as `IEnumerable<int>` in C#
    * F# `ResizeArray<int>` -- same as `List<int>` in C#
    * F# arrays `int[]` -- same as `Array<int>` in C#


    * Have a CSharpHelper module with useful functions

  25. @swlaschin swlaschin revised this gist Apr 23, 2019. 1 changed file with 1 addition and 4 deletions.
    5 changes: 1 addition & 4 deletions effective-fsharp.md
    Original file line number Diff line number Diff line change
    @@ -125,7 +125,7 @@ See
    * https://github.com/swlaschin/DomainModelingMadeFunctional/blob/master/src/OrderTaking/PlaceOrder.Dto.fs#L120


    ## interacting with C#
    ## Interacting with C#

    * Avoid exposing F# types if possible
    * NOTE: If `Fsharp.Core.dll` is missing, use nuget to add package
    @@ -168,9 +168,6 @@ Create a `Match` function for each choice type you expose, with a `Func` for eac

    ```fsharp
    module Result =
    let okExample :Result<string,ExampleErrorType> = Ok "ok"
    let errorExample :Result<string,ExampleErrorType> = Error (BadConnection "error")
    let Match(result, onOk:Func<_,_>, onError:Func<_,_>) =
    match result with
    | Ok x -> onOk.Invoke(x)
  26. @swlaschin swlaschin revised this gist Apr 23, 2019. 1 changed file with 58 additions and 0 deletions.
    58 changes: 58 additions & 0 deletions effective-fsharp.md
    Original file line number Diff line number Diff line change
    @@ -123,3 +123,61 @@ let toDomainObj dto =

    See
    * https://github.com/swlaschin/DomainModelingMadeFunctional/blob/master/src/OrderTaking/PlaceOrder.Dto.fs#L120


    ## interacting with C#

    * Avoid exposing F# types if possible
    * NOTE: If `Fsharp.Core.dll` is missing, use nuget to add package

    ### Exposing an API to C#

    * Expose API in a .NET friendly manner. Add a module called either `Api` or `Api.Csharp`
    * Understand tuple-style vs curried functions. Expose tuple-style only.

    ```fsharp
    module Api =
    // correct
    let DoSomething(x,y,x) =
    // incorrect
    let DoSomething x y x =
    ```

    * Use C# collections (seq or Array or ResizeArray) not F# list

    * Have a CSharpHelper module with useful functions

    ```fsharp
    module List =
    // IEnumerable to F# list
    let EnumToList enum = enum |> List.ofSeq
    // IEnumerable from F# list
    let EnumFromList list = list |> List.toSeq
    ```

    * Use Func<> instead of F# functions
    * NOTE: F# Async needs to be converted to C# Task
    * NOTE: F# `float` == C# `double`
    * NOTE: C# Enums are not the same as choice/union types
    * Don't expose tuples in Api

    ### Exposing choice types to C#

    Create a `Match` function for each choice type you expose, with a `Func` for each case. Here's the one for `Result`:

    ```fsharp
    module Result =
    let okExample :Result<string,ExampleErrorType> = Ok "ok"
    let errorExample :Result<string,ExampleErrorType> = Error (BadConnection "error")
    let Match(result, onOk:Func<_,_>, onError:Func<_,_>) =
    match result with
    | Ok x -> onOk.Invoke(x)
    | Error e -> onError.Invoke(e)
    ```





  27. @swlaschin swlaschin revised this gist Apr 23, 2019. 1 changed file with 6 additions and 1 deletion.
    7 changes: 6 additions & 1 deletion effective-fsharp.md
    Original file line number Diff line number Diff line change
    @@ -71,8 +71,13 @@ Define them in their own module.
    For each DTO, define an associated module with functions "toDomain" and "fromDomain"

    ```fsharp
    type MyDto = ...
    namespace CurrentAwareness.Dto
    type MyDto = {
    Something: string
    }
    /// could be in different module if you want to hide the implementation
    module MyDto =
    // may fail if DTO has bad data
  28. @swlaschin swlaschin revised this gist Apr 23, 2019. 1 changed file with 59 additions and 2 deletions.
    61 changes: 59 additions & 2 deletions effective-fsharp.md
    Original file line number Diff line number Diff line change
    @@ -1,11 +1,68 @@
    ## Modules and namespaces

    * A namespace is just like C#
    * A "module" is like a C# class with only static methods

    If defining types only (eg domain), use a namespace.
    If defining functions, they *MUST* be in a module (otherwise youe get a compiler error)

    For functions closely associated with a type (eg `create`/`value`), use a module with the same name as the type.

    NOTES:
    * You can't use namespaces in F# interactive
    * You can use "rec" for recursive modules so that the definitions don't have to be in order.


    ## Signature files

    * with `fsi` file extension.
    * have `fsi` file extension.
    * must be above the corresponding `fs` file.

    | | FSI | FS |
    ```
    | | FSI | FS |
    | top of file | namespace/module | same as FSI |
    | types | public | public types must be defined the same |
    | | | private types OK |
    | functions | use "val" | use "let" |
    ```

    Example signature file

    ```fsharp
    /// Signature file
    module CurrentAwareness.Dto.Converter
    open CurrentAwareness
    type DtoError =
    | BadName
    module CuratedItem =
    val fromDomain : CuratedItem -> CuratedItemDto
    val toDomain : CuratedItemDto -> Result<CuratedItem,DtoError>
    ```

    Example implementation file

    ```fsharp
    module CurrentAwareness.Dto.Converter
    open CurrentAwareness
    type DtoError =
    | BadName
    type MyPrivateType = string
    module CuratedItem =
    let fromDomain (x:CuratedItem) :CuratedItemDto =
    failwith "not implemented"
    let toDomain (x:CuratedItemDto) :Result<CuratedItem,DtoError> =
    failwith "not implemented"
    ```


    ## DTOs and Validation
  29. @swlaschin swlaschin revised this gist Apr 23, 2019. 1 changed file with 10 additions and 0 deletions.
    10 changes: 10 additions & 0 deletions effective-fsharp.md
    Original file line number Diff line number Diff line change
    @@ -1,3 +1,13 @@

    ## Signature files

    * with `fsi` file extension.
    * must be above the corresponding `fs` file.

    | | FSI | FS |
    | top of file | namespace/module | same as FSI |


    ## DTOs and Validation

    Define them in their own module.
  30. @swlaschin swlaschin revised this gist Apr 23, 2019. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion effective-fsharp.md
    Original file line number Diff line number Diff line change
    @@ -50,4 +50,4 @@ let toDomainObj dto =
    ```

    See
    * https://github.com/swlaschin/DomainModelingMadeFunctional/blob/master/src/OrderTaking/PlaceOrder.Dto.fs
    * https://github.com/swlaschin/DomainModelingMadeFunctional/blob/master/src/OrderTaking/PlaceOrder.Dto.fs#L120