andre-dietrich / elm-generic / Generic

This module defines a basic type system, as it is known from JavaScript. The basic idea is that you can use generic types of decoders that can translate into this format, so you are not forces anymore to write custom decoders if your are only interested in some basic values.

The type system should also behave similarly the one we know from JavaScript.

Types


type Value
    = Null
    | Bool Basics.Bool
    | Int Basics.Int
    | Float Basics.Float
    | String String
    | List (List Value)
    | Set (EverySet Value)
    | Dict (GenericDict.Dict Value Value)
    | Date Time.Posix
    | DateTime Time.Posix

Supported basic types. In contrast to JSON types, this covers also types such as Set, Date, and DateTime.

In the future it would also be interesting to have a general binary type that can be used with any kind of transformation function, i.e. toFloat or for serializing streams of data (lists, sets, etc.).

Sets and Dicts use different types than the Base elm-types, so that they can cope with more complex values that Int, String, etc. Thus, a Generic.Dict can also have keys of type integer or list, nested ones are also allowed.

typeOf : Value -> String

Clone of the JavaScript function typeof that returns a String:

typeOf Null --> "null"

typeOf (Bool True) --> "bool"

typeOf (Int -99) --> "int"

typeOf (Float 1.234) --> "float"

typeOf (String "foo") --> "string"

Getting & Setting

get : List Value -> Value -> Maybe Value

Generic getter, the first parameter defines a sequence of how the generic type should be traversed. The result is a Maybe Value that needs to be converted into your desired elm-type:

import Generic.Decoder exposing (decode)

"{\"type\": [1, [2, \"tada\"] ]}"
    |> decode
    |> Result.withDefault Null
    |> get [String "type", Int 1, Int 1]
    |> Maybe.andThen toString
    |> (==) (Just "tada") --> True

"{\"type\": [1, [2, \"tada\"] ]}"
    |> decode
    |> Result.withDefault Null
    |> get [Int 99]
    |> Maybe.andThen toString
    |> (==) Nothing --> True

set : List Value -> Value -> Value -> Value

Generic setter, the first parameter defines a sequence of how the generic type should be traversed and the second the new value

import Generic.Json as Json
import Generic as Gen

"{\"type\": [1, [2, \"tada\"] ]}"
    |> Json.decodeString
    |> Gen.set [Gen.String "type", Gen.Int 1, Gen.Int 1] (Gen.Int 9999)
    |> Json.encode
    |> Json.toString 0
    |> (==) "{\"type\":[1,[2,9999]]}"
    --> True

"{\"type\": [1, [2, \"tada\"] ]}"
    |> Json.decodeString
    |> Gen.set [Gen.String "type", Gen.Int 11, Gen.Int 11] (Gen.Int 9999)
    |> Json.encode
    |> Json.toString 0
    |> (==) "{\"type\":[1,[2,\"tada\"]]}"
    --> True

Transformations

toBool : Value -> Basics.Bool

Same as in JavaScript, tries to make a bool from every type

toBool Null --> False

toBool (Bool True) --> True

toBool (Bool False) --> False

toBool (Int 0) --> False

toBool (Int -1) --> True

toBool (Float 0.0) --> False

toBool (Float 3.141592) --> True

toBool (String "") --> False

toBool (String "misc") --> True

toBool (List []) --> True

toInt : Value -> Maybe Basics.Int

Convert a generic toy to Int, if possible

toInt Null --> Nothing

toInt (Bool True) --> Just 1

toInt (Bool False) --> Just 0

toInt (Int 0) --> Just 0

toInt (Int -1) --> Just -1

toInt (Float 0.0) --> Just 0

toInt (Float 3.141592) --> Just 3

toInt (String "") --> Nothing

toInt (String "33") --> Just 33

toInt (String "33.33") --> Just 33

toFloat : Value -> Maybe Basics.Float

Convert a generic type to Float, if possible

import Generic as Gen

Gen.toFloat Null --> Nothing

Gen.toFloat (Bool True) --> Just 1.0

Gen.toFloat (Bool False) --> Just 0.0

Gen.toFloat (Int 2) --> Just 2.0

Gen.toFloat (Int -1) --> Just -1.0

Gen.toFloat (Float 0.0) --> Just 0.0

Gen.toFloat (Float 3.141592) --> Just 3.141592

Gen.toFloat (String "") --> Nothing

Gen.toFloat (String "33") --> Just 33

Gen.toFloat (String "33 m") --> Nothing

Gen.toFloat (String "33.33") --> Just 33.33

toString : Value -> Maybe String

Convert a generic type to a string representation. Actually this works for any value, except for Null:

toString Null --> Nothing

toString (Bool True) --> Just "true"

toString (Bool False) --> Just "false"

toString (Int 2) --> Just "2"

toString (Int -1) --> Just "-1"

toString (Float 0.0) --> Just "0"

toString (Float 3.141592) --> Just "3.141592"

toString (String "") --> Just ""

toString (String "33") --> Just "33"

toString (String "33 m") --> Just "33 m"

toString (String "33.33") --> Just "33.33"

toString (List [ String "33.33", Null ]) --> Just "[\"33.33\",null]"

toList : Value -> Maybe (List Value)

Tries to turn every type into a list

import Generic as Gen

List [ Null, Int 12 ] |> toList --> Just [Null, Int 12]

List [ Null, Int 12, Int 12]
    |> toSet
    |> Set
    |> toList --> Just [Int 12, Null]

toDict : Value -> Maybe (Dict String Value)

Returns an elm Dict from a Generic.Dict, if possible

toSet : Value -> EverySet Value

Turns everything into a set of type EverySet, if possible. Passing a Dict will result in a set of dict values.

toDate : Value -> Maybe Time.Posix

Try to get a Date value in Posix Format.

toDateTime : Value -> Maybe Time.Posix

Try to get a DateTime value in Posix Format.

Helpers

dictFromList : List ( Value, Value ) -> Value

Turns a list of tuples of generic values into a generic dictionary.

[ ( Int 12, String "foo" ), ( Null, List [ Float 12.0, Null ] ) ]
    |> dictFromList