elm - executing multiple lines in a function - functional-programming

In elm, is something like the below possible
foo : Int -> Html
foo inputNum =
addToNumHistory inputNum ;
display inputNum
where the aim of the above is to execute multiple lines of code?
If not, is this because the above is an example of a side effect?
If something like the above syntax is not possible, how would one go about executing two functions/lines of code simultaneously, as in the above, or as a result of a given input (case branch) ?
Edit
The above is a bad example. The following uses the Elm Architecture:
--Model
type alias Model =
{ number : Int
, numberHistory : List Int
}
type Action
= Number Int
--Update
update : Action -> Model
update action =
case action of
Number num->
addToNumHistory num
addToNumHistory : Int -> Model -> Model
addToNumHistory num modelHistory =
{ model
| number = num
, numberHistory = num :: model.numberHistory
}
--View
view : Signal.Address Action -> Model -> Html
view action model =
div []
[ field
"text"
address
Number model.number
"Enter lucky number here pal!"
model.number
]
Given this, am I right in presuming that to 'execute multiple lines' in such a fashion as to alter an underlying model, one would simply use/extend the model - for example, to effect a change analogous to the following:
--Update
update : Action -> Model
update action =
case action of
Number num->
addToNumHistory num;
addToChangeHistory
one would simply extend the model as follows:
--Model
type alias Model =
{ number : Int
, numberHistory : List Int
, changeHistory : List Date
}
--Update
update : Action -> Model
update action =
case action of
Number num->
addToNumHistoryWithChangeHistory num
addToNumHistoryWithChangeHistory : Int -> Model -> Model
addToNumHistory num modelHistory =
{ model
| number = num
, numberHistory = num :: model.numberHistory
, changeHistory = getCurrentDate :: model.changeHistory
}
getCurrentDate : Date

In this specific case, you don't need to have side-effects.
I've had to add two utility functions to create a functioning example.
onInput to handle 'input' event
parseInt to retrieve Int from a String
The rest is a basic Elm Architecture lifecycle as of 0.16
Please consider this minimal example I made for use with StartApp.Simple:
import Html exposing (text, input, div, Html, Attribute)
import Html.Attributes exposing (value)
import Html.Events exposing (on, targetValue)
import String
import Signal exposing (Address)
import StartApp.Simple as StarApp
--Utils
onInput : Address a -> (String -> a) -> Attribute
onInput address f =
on "input" targetValue (\v -> Signal.message address (f v))
parseInt : String -> Int
parseInt string =
case String.toInt string of
Ok value ->
value
Err error ->
0
--Model
type alias Model =
{ number : Int
, numberHistory : List Int
}
initModel : Model
initModel =
{ number = 0
, numberHistory = []
}
--Update
type Action
= UpdateNumber String
update : Action -> Model -> Model
update action model =
case action of
UpdateNumber num ->
addToNumHistory (parseInt num) model
addToNumHistory : Int -> Model -> Model
addToNumHistory num model =
{ model
| number = num
, numberHistory = num :: model.numberHistory
}
--View
view : Signal.Address Action -> Model -> Html
view address model =
div
[]
[ input
{- On every 'input' event,
grab the value of input field and send to UpdateNumber
-}
[ onInput address UpdateNumber, value (toString model.number) ]
[]
, div [] [ text (toString model.number) ]
, div
[]
( model.numberHistory
|> List.reverse
|> List.map (toString)
|> List.map text
)
]
main : Signal Html
main =
StarApp.start
{ view = view
, update = update
, model = initModel
}

Related

How to handle messages from recursive HTML UI in Elm?

I'm trying to build a UI that allows a user to manipulate a recursive data structure. For example, imagine a visual schema editor or database table editor in which you have plain old types (strings and integers) and compound types made up of those plain types (arrays, structs). In the example below, a Struct_ is like a JavaScript object, where the keys are strings and the values are any type, including nested Array_s and Struct_s.
-- underscores appended to prevent confusion about native Elm types. These are custom to my application.
type ValueType
= String_
| Int_
| Float_
| Array_ ValueType
| Struct_ (List (String, ValueType))
type alias Field =
{ id : Int
, label : String
, hint : String
, hidden : Bool
, valueType : ValueType
}
type alias Schema = List Field
Now to go about building a UI for this I can make a simple recursive function:
viewField : Field -> Html Msg
viewField field =
div []
[ input [ type_ "text", value field.label ] []
, viewValueType field.valueType
]
viewValueType : ValueType -> Html Msg
viewValueType valueType =
let
structField : (String, ValueType) -> Html Msg
structField (key, subtype) =
div []
[ input [type_ "text", placeholder "Key", value key, onInput EditStructSubfieldKey] []
, viewValueType subtype
]
options : List(Html Msg)
options = case valueType of
String_ -> -- string ui
Int_ -> -- int ui
Float_ -> -- float ui
Array_ subtype ->
[ label [] [ text "subtype" ]
, viewValueType subtype
]
Struct_ fields ->
[ label [] [ text "subfields" ]
, List.map structField fields
, button [ onClick AddStructSubfield ] [ text "Add subfield" ]
]
in
div [] options
My issue arises when trying to manipulate my state with this recursive structure. What data structure in a Msgs would accommodate user edits to this structure, adding new fields, subfields, and editing their properties? How would I properly decode that in my update loop?
For example...
type alias Model =
{ fields : List Field }
update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
case msg of
AddStructSubfield _???_ ->
({model | fields = ???}, Cmd.none)
EditStructSubfieldKey _???_ ->
({model | fields = ???}, Cmd.none)
What kind of data would you attach to that AddStructSubfield or EditStructSubfieldKey message (that's passed with the onClick handler to the button above) to properly update your state, specifically when the Struct_ is say, nested inside of another Struct_, nested inside of an Array_? EditStructSubfieldKey, for example, will only contain the new string that the user has entered, but not enough information to address a deeply-nested item.
We do exactly this in our code base, but haven't open sourced the 'library' that supported this. But the answer to your question is that you need to add the notion of a Path to your code and messages.
type Path
= Field: String
| Index: Int
Then your view has to keep updating the path as you descend [Field "f1", Index 3, ...], and your update function needs to be supported by insert, delete,... that take a Path and the existing structure and return you a new one.
I ended up solving this by passing an updater function down the recursive chain. I've simplified this example as much as possible while showing the recursive nature of the updating. This allows for updating infinitely nested structures and lists without worrying about encoding/decoding a path. The downside, I believe, is that my single update Msg will always replace the entire model. I'm not sure about the semantics of how this will affect Elm's equality checking, and if that will produce performance issues in certain applications.
This example can be copy/pasted into https://elm-lang.org/try as-is to see it in action.
import Html exposing (Html, div, input, ul, li, text, select, button, option)
import Html.Attributes exposing (value, type_, selected)
import Html.Events exposing (onInput, onClick)
import Browser
type ValueType
= String_
| Int_
| Array_ ValueType
| Struct_ (List Field)
type alias Field =
{ label : String
, valueType : ValueType
}
type alias Model = Field
main = Browser.sandbox { init = init, update = update, view = view }
init : Model
init =
{ label = "Root Field", valueType = String_ }
type Msg
= UpdateField Field
update : Msg -> Model -> Model
update msg model =
case msg of
UpdateField field ->
field
view : Model -> Html Msg
view model =
let
updater : Field -> Msg
updater field =
UpdateField field
in
div [] [ viewField updater model ]
viewField : (Field -> Msg) -> Field -> Html Msg
viewField updater field =
let
updateLabel : String -> Msg
updateLabel newLabel =
updater {field | label = newLabel}
updateValueType : ValueType -> Msg
updateValueType newValueType =
updater {field | valueType = newValueType}
in
li []
[ input [ type_ "text", value field.label, onInput updateLabel ] [ ]
, viewTypeOptions updateValueType field.valueType
]
viewTypeOptions : (ValueType -> Msg) -> ValueType -> Html Msg
viewTypeOptions updater valueType =
let
typeOptions = case valueType of
String_ ->
div [] []
Int_ ->
div [] []
Array_ subtype ->
let
subUpdater : ValueType -> Msg
subUpdater newType =
updater <| Array_ newType
in
div [] [ div [] [ text "Subtype" ], viewTypeOptions subUpdater subtype ]
Struct_ fields ->
let
fieldAdder : Msg
fieldAdder =
updater <| Struct_ ({label = "", valueType = String_} :: fields)
fieldUpdater : Int -> Field -> Msg
fieldUpdater index newField =
updater <| Struct_ <| replaceInList index newField fields
in
div []
[ ul [] (List.indexedMap (\i -> (viewField <| fieldUpdater i)) fields)
, button [ onClick fieldAdder ] [ text "+ Add Field" ]
]
isArray t = case t of
Array_ _ -> True
_ -> False
isStruct t = case t of
Struct_ _ -> True
_ -> False
stringToType str = case str of
"string" -> String_
"int" -> Int_
"array" -> Array_ String_
"struct" -> Struct_ []
_ -> String_
changeType str =
updater <| stringToType str
in
div []
[ select [ onInput changeType ]
[ option [ value "string", selected <| valueType == String_ ] [ text "String" ]
, option [ value "int", selected <| valueType == Int_ ] [ text "Integer" ]
, option [ value "array", selected <| isArray valueType ] [ text "Array" ]
, option [ value "struct", selected <| isStruct valueType ] [ text "Struct" ]
]
, typeOptions
]
replaceInList : Int -> a -> List a -> List a
replaceInList index item list =
let
head = List.take index list
tail = List.drop (index+1) list
in
head ++ [ item ] ++ tail

How to communicate with a polymorphic child component in Elm?

My main program has an update function of
update : Msg -> Model -> ( Model, Cmd Msg )
To communicate with sub-components we can add another variant and wrap our messages in a new message
type alias Model =
{ ...
, child : Child.Model
}
type Msg
= ...
| ChildMsg Child.Msg
update msg model =
case msg of
...
ChildMsg childMsg ->
let
( childModel, cmd ) =
Child.update childMsg model.child
updatedModel =
{ model | child = childModel }
childCmd =
Cmd.map ChildMsg cmd
in
( updatedModel, childCmd )
However this seem challenging if the type of my sub-component's update function does not match the parent. Consider a child with a polymorphic update function:
-- PolymorphicChild.elm
update : Msg a -> Model -> ( Model, Cmd (Msg a) )
When running a command from this module, I must wrap it
PolymorphicChild.someCommand : Cmd (Msg Foo)
PolymorphicChild.someCommand
|> Cmd.map PolymorphicChild
However, this produces a Msg (PolymorphicChild.Msg Foo), not the Msg PolymorphicChild.Msg my App is expecting.
The right side of (|>) is causing a type mismatch.
(|>) is expecting the right side to be a:
Cmd (PolyMorphicChild.Msg Foo) -> a
But the right side is:
Cmd Polymorphic.Msg -> Cmd Msg
I tried adding a polymorphic parameter to App.Msg
-- App.elm
type Msg a =
= ..
| PolymorphicChildMsg (PolymorphicChild.Msg a)
But it basically blows up my entire program. Every function involving App.Msg needs to somehow be changed to work with the new child component.
How can I unify the two types and get the two components working together?
I think the problem is that you're leaking too much information in your publicly exposed Msg type. Your use of the type parameter of Msg a seems limited to a known set of types, either an Author, Category, Post, or Tag. From skimming your code, it looks like it will never be anything but one of those four, so the fact that you are abstracting things in this manner should be kept inside of this module rather than exposing it and burdening any other code that may be pulling this in.
I think you need to move the abstraction down a level to avoid parameterizing your public Msg type. I would suggest having four concrete constructors for Msg instead of parameterizing it, and shift the abstraction down to a helper LoadInfo a type:
type alias LoadInfo a =
{ worker : Worker a
, url : Url
, result : Result Http.Error ( Int, List a )
}
type Msg
= LoadPost (LoadInfo Post)
| LoadCategory (LoadInfo Category)
| LoadTag (LoadInfo Tag)
| LoadAuthor (LoadInfo Author)

How to recur over extensible records?

I am a relative beginner in Elm. I learned how to recur over records, and to create extensible records. My target application has tree-shaped data, that is to be rendered in several tree views.
In order for Elm to allow recursive models, one needs
a Union type for the children field,
a Maybe in that type definition, and
the lazy anonymous function construct if Json decoding is needed.
So far so good, but I cannot combine recursion with extension. Here below is a minimized version of how far I got. This program is capable of rendering either one of the two example types (Folder and Note), but not both. (see lines commented with -- NOTE and -- FOLDER).
The problem is with the kids function. Elm doesn't allow it to produce two different output types. I am stuck with either duplicating the code, or do without record extensions. Both seems like show-stoppers.
Is there a way to get this working with both, extension and recursion, and without code duplication?
Run on Ellie
module Main exposing (main)
import Html exposing (..)
import Maybe
-- MAIN
main = Html.beginnerProgram
{ model = init
, update = update
, view = view
}
-- MODEL
type alias Model =
{ folder : Folder
, note : Note
}
type alias Node a =
{ a | name : String
, children : Children a
}
type alias Folder =
{ name : String
, children : ChildFolders
}
type alias Note =
{ name : String
, children : ChildNotes
}
type Children a = Children a (Maybe (List (Node a)))
type ChildFolders = ChildFolders (Maybe (List Folder))
type ChildNotes = ChildNotes (Maybe (List Note))
-- INIT
init : Model
init = Model
(Folder "Parent F" someFolders)
(Note "Parent N" (ChildNotes Nothing))
someFolders : ChildFolders
someFolders = ChildFolders
( Just
( [ Folder "Child F1" (ChildFolders Nothing)
, Folder "Child F2" (ChildFolders Nothing)
, Folder "Child F3" (ChildFolders Nothing)
]
)
)
-- UPDATE
type Msg = NoOp
update : Msg -> Model -> Model
update msg model =
case msg of
NoOp -> model
-- VIEW
view : Model -> Html msg
view model =
div []
[ viewBranch model.folder -- FOLDER
-- , viewBranch model.note -- NOTE
]
-- viewBranch : (?) -> Html msg
viewBranch node =
uli
( text node.name
:: ( node
|> kids
|> List.map viewBranch
)
)
uli : List (Html msg) -> Html msg
uli items = ul [] [ li [] items ]
-- kids : (?) -> (?)
kids { children } =
case children of
(ChildFolders data) -> Maybe.withDefault [] data -- FOLDER
-- (ChildNotes data) -> Maybe.withDefault [] data -- NOTE

How does the Bool from `targetChecked` get turned into an `Action`?

In the Elm checkboxes example an Action is passed to the tag argument of the checkbox function (lines 51-53).
I don't understand how the type signature for this argument is (Bool -> Action) and how on line 69 it's able to use the function composition operator << to transform the Bool from targetChecked into the complete Action type.
EDIT:
This question can be reduced down to "why does the following work?"
type Action = Edit Int
do : (Int -> Action) -> Action
do tag = tag(123)
result : Action
result = do(Edit)
When you define a union type, each tag of the union type becomes a defined value. So when you define:
type Action = Tick | NoOp
this also defines:
Tick : Action
NoOp : Action
When the union tag has arguments, it becomes a "constructor", a function:
type Action = Edit Int
Edit : Int -> Action
(These tags are also used as patterns that you can match on with the case-of construct. See also the documentation on the website. )

How to access a field of a type defined in an OCaml recursive module?

I am working on recursive modules in OCaml and I have some trouble accessing type fields.
If I try to do :
module A = struct type t = { name : string; } end
module A2 =
struct
include A
let getName (x:t) = x.name
end;;
Everything is alright. However, I need a more complex type, forcing me to define my type in a recursive module.
module rec B:Set.OrderedType =
struct
type t = {name: string; set : S.t}
let compare _ _ = 0
end
and S:Set.S = Set.Make (B);;
Everything still works perfectly. However, the following module is incorrect :
module B2 =
struct
include B
let get_name (x:t) = x.name
end;;
The returned error is "Unbound record field name". What is the problem ?
module rec B:Set.OrderedType =
Your recursive definition says that module B has the signature Set.OrderedType, which hides the definition of t and in this case, its projections. In Set.OrderedType, the type t is abstract, like this: type t;;.
If you want to show the definition of type t, it must be part of the signature. The first example works because you did not offer a signature for module A, so it was typed by default with a signature that exports everything.
The example below works for me with OCaml 4.02.1.
module rec B:
sig type t = { name:string ; set : S.t } val compare: t -> t -> int end
=
struct
type t = {name: string; set : S.t}
let compare _ _ = 0
end
and S:Set.S = Set.Make (B);;
The toplevel confirms the definition thus:
module rec B :
sig type t = { name : string; set : S.t; } val compare : t -> t -> int end
and S : Set.S

Resources