Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Creating APIs in Elm

Creating APIs in Elm

APIs are a fundamental part of building software. This talk introduces patterns and tips for creating APIs in Elm. Taking advantage of Elm’s type system and functional purity can create modules that are a joy to use. Whether you are looking to publish an Elm package or just improve your own codebase, creating better APIs can help.

Michael Torres

July 05, 2018
Tweet

More Decks by Michael Torres

Other Decks in Programming

Transcript

  1. Focused Focused on an idea or solution to a problem

    Avoid bloat and unnecessary complexity
  2. Opinionated Takes a stance on how to do things Less

    thinking for consumers Designed specifically for their stance
  3. Type System Compiler enforcement of safe APIs Force handling of

    possible failures Adds constraints of API usage Type signatures help document
  4. Conventions Language designed for simplicity Opinionated language and packages Documentation

    examples are encouraged Community encouragement for new ideas and exploration
  5. Opaque Type A data type whose implementation is not visible

    to the consumer Functions are used to access and update data within the opaque type
  6. module Module exposing (MyOpaqueType) type alias Data = { internals

    : Int } type MyOpaqueType = MyOpaqueType Data Opaque Type
  7. module Module exposing (MyOpaqueType) type alias Data = { internals

    : Int } type MyOpaqueType = MyOpaqueType Data Opaque Type
  8. module Module exposing (MyOpaqueType) type alias Data = { internals

    : Int } type MyOpaqueType = MyOpaqueType Data Opaque Type
  9. module Module exposing (MyOpaqueType) type alias Data = { internals

    : Int } type MyOpaqueType = MyOpaqueType Data Opaque Type
  10. module Game.Store exposing (Store) type alias StoreInfo = { games

    : Dict String Game , orders : Dict String Order } type Store = Store StoreInfo Store Example
  11. Occurrence: When to use You want to hide details within

    a module Want flexibility to change internals Often
  12. Type Variable A representation of any type when given to

    a function or type Same concept as “generics”
  13. withInt : MyType Int withInt = WithVariable Int withString :

    MyType String withString = WithVariable String Type Variable
  14. Benefits Lets us write functions and types that can work

    on many types Use Elm’s built-in typeclasses (appendable, comparable, number)
  15. Occurrence: When to use Need to work with an unbounded

    amount of types Allow the consumer to declare the type you work with Need to use Elm’s built in typeclasses Occasionally
  16. required : { a | myRequiredField : Int } !->

    Int required { myRequiredField } = myRequiredField Extensible Record
  17. required : { a | myRequiredField : Int } !->

    Int required { myRequiredField } = myRequiredField Extensible Record
  18. type alias Data = { name : String , id

    : Int … , myRequiredField : Int … } result = required data Extensible Record
  19. Benefits Functions can work with just the data it needs

    Ability to work on different records Can reduce mapping to new types
  20. type alias Item a = { a | name :

    String , price : String , thumbUrl : String } itemView : Item a !-> Html msg itemView { name, price, thumbUrl } = !!... Store Example
  21. type alias Game = { id : String , name

    : String , price : String , publisher : String , rating : Int , thumbUrl : String } type alias GiftCard = { name : String , price : String , thumbUrl : String , amount : Float } itemView game itemView giftCard Store Example
  22. Occurrence: When to use You don’t care about what record

    type is used You think it would be easier for consumers to pass in their existing records Occasionally
  23. Phantom Type A type that declares some type variables that

    are not used in the type definition They add more information for the compiler to the types
  24. Benefits Can be used to enforce some extra compile time

    checks Functions can be used to accept only a variation of the type
  25. type Price a = Price Float type USD = USD

    type EUR = EUR Price USD Price EUR Store Example
  26. usdOnlyOperation : Price USD !-> Price USD usdOnlyOperation price =

    … add : Price a !-> Price a !-> Price a add (Price x) (Price y) = Price (x + y) Store Example
  27. Occurrence: When to use You have runtime values that can

    be compile checked instead You want compile time checks on variations of a single type Rarely
  28. type alias Params = { minRating : Maybe Int ,

    maxRating : Maybe Int , onSale : Bool } defaultParams : Params defaultParams = { minRating = Nothing , maxRating = Nothing , onSale = false } Records
  29. type Params = Params Params init : Params init =

    Params defaultParams onSale : Bool !-> Params !-> Params onSale sale (Params params) = Params { params | onSale = sale } maxRating : Int !-> Params !-> Params maxRating rating (Params params) = Params { params | maxRating = Just rating } Functions
  30. type Param = OnSale Bool | MaxRating Int | MinRating

    Int onSale : Bool !-> Param onSale sale = OnSale sale minRating : Int !-> Param minRating min = MinRating min List of Options
  31. request : List Param !-> Task Error Response request params

    = !!... request [ onSale True, minRating 5 ] List of Options
  32. Handling Side-Effects Provide Tasks and Cmds Provide your own Sub

    Provide your own Msg type and update function
  33. getGame : String !-> Task Error Game getGame id =

    … getGames : Task Error (List Game) getGames = … order : Order !-> Cmd msg order myOrder = !.. Tasks and Cmds
  34. type Msg = AddGame Game | Order Order update :

    Msg !-> Store !-> (Store, Cmd Msg) update msg store = !!... Update function
  35. View APIs Rely on functions, not “components” Only maintain state

    for the view. Consumers provide data Can have a “Config” record for the view function
  36. type alias Config msg = { toId : Game !->

    String , toMsg : State !-> msg } view : Config msg !-> Game !-> State !-> Html msg view config game state = !!... Config
  37. type alias AdvancedConfig msg = { toId : Game !->

    String , toMsg : State !-> msg , color : String , isImportant : Bool } customView : AdvancedConfig msg !-> Game !-> State !-> Html msg customView config game state = !!... Customization
  38. module Game.Store exposing (Store, addToCart, remove) {-| This is a

    module to help purchase games. # Opaque Type that represents a Store @docs Store !## Buying games @docs addToCart, remove -} Document Module
  39. {-| Add a game to the cart. This is required

    before purchasing. See the full example [here] (https:!//mycoolexample.com). addToCart selectedGame store -} addToCart : Game !-> Store !-> Store addToCart game store = !!... Document Function
  40. Tips Can design with just types first (Type-Driven Development) Never

    use Debug.crash after release Nice APIs take time. Iteration is common Use your own API and gather feedback
  41. Learn More Official Design Guidelines 
 http://package.elm-lang.org/help/design-guidelines Existing Packages
 elm-sortable-table,

    elm-bounded-number, elm-css
 
 Elm Slack
 #package-design
 
 Elm Town Podcast
 https://www.elmtown.audio/