How I can pass to a function the union type without including the tag - elm

Playing with the Elm checkboxes example. I am trying to move the following repetative code in view
, label []
[ br [] []
, input [ type' "checkbox", checked model.underline, onCheck Underline ] []
, text "underline"
]
into a separate function and use it three times. So far I have ...
makeLabel : String -> Bool -> Msg -> Html Msg
makeLabel caption bool msg =
label []
[ br [] []
, input [ type' "checkbox", checked bool, onCheck msg ] []
, text caption
]
and I would use it like
makeLabel "underline" model.underline Underline
but then I receive the following error
Function `makeLabel` is expecting the 3rd argument to be:
Msg
But it is:
Bool -> Msg
How do I pass my makeLabel function the correct action to take when a user changes the checkbox?
type Msg
= Red Bool
| Underline Bool
| Bold Bool
I don't understand how I can pass to a function the union type (Underline) without including the tag (Underline Bool)

The problem is in your type signature, rather than the code. Try this:
makeLabel : String -> Bool -> (Bool -> Msg) -> Html Msg

Related

How to make uppercase records in purescript?

I have an API that forces me to return a JSON in the form of
{ Field1: "something"
, Field2: 12 }
However, I have failed to model this in Purescript so far.
I understand that the purescript grammar sees the upper case field names and thinks it is the type of a field without a field name. So I it is not straight forward to just encode a function like
test :: Number -> { Field1 :: String, Field2 :: Number }
Without resorting to using external javascript functions to change the object into something else, is it at all possible to construct a record with upper case field names in purescript? And if so, how?
Absolutely! PureScript allows any field names, not just "valid identifier" ones. You just have to double-quote them if they're weird:
x :: { "Field1" :: String, "Field2" :: String, "🍕" :: String }
x = { "Field1": "foo", "Field2": "bar", "🍕": "yum" }
y :: String
y = x."🍕"
Basically same deal as in JavaScript.

Elm View Function Returns a Function Instead of the Value of it

The Elm Compiler gives me this error in my view function ...
TYPE MISMATCH - Something is off with the 2nd branch of this case expression:
div [] [
div [] [text (String.fromInt value)],
div [] [button [ onClick identity ] [ text "Enter number" ]]]
This div call produces:
Html #(a -> a)#
But the type annotation on view says it should be:
Html #Msg#Elm
Does anyone know how I can fix this issue?
Thank you!
module Main exposing (..)
import Browser
import Html exposing (Html, button, div, text)
import Html.Events exposing (onClick)
-- MAIN
main =
Browser.sandbox { init = init, update = update, view = view }
-- MODEL
type alias Model =
Result String Int
init : Model
init =
Result.Ok 0
-- UPDATE
type alias Msg = Int
update : Msg -> Model -> Model
update msg model =
collatz msg
collatz : Int -> Result String Int
collatz start =
if start <= 0 then
Result.Err "Only positive numbers are allowed"
else
Result.Ok (collatzHelper start 0)
collatzHelper : Int -> Int -> Int
collatzHelper start counter =
if start == 1 then
counter
else if modBy 2 start == 0 then
collatzHelper (start // 2) (counter + 1)
else
collatzHelper (3 * start + 1) (counter + 1)
-- VIEW
view : Model -> Html Msg
view model =
case model of
Err err ->
div [] [text err]
Ok value ->
div [] [
div [] [text (String.fromInt value)],
div [] [button [ onClick identity ] [ text "Enter number" ]]]
The compiler error message, as always with Elm, is fairly straightforward to understand. The problem is the onClick attribute in this line:
div [] [button [ onClick identity ] [ text "Enter number" ]]
As the compiler says, this has type a -> a, when it's expected to have type Msg, which in your case is the same as Int. That is, you've passed a function and it should be an integer.
Since value here is indeed an Int, that seems the natural choice to use here:
div [] [button [ onClick value ] [ text "Enter number" ]]
and this does indeed compile. However, this app doesn't seem particularly useful, because the user has no way to change the value displayed. You will need to include some sort of numeric input in your app to allow that - and then you will need a new alternative in your Msg type to handle the change of value.
Judging by this and your previous question (which is where I assume you got the idea of using identity as an onClick value - unfortunately that doesn't work in general), you seem to be struggling a bit with how the Elm Architecture works, so if you haven't already I would strongly recommend going through the tutorial.

Is there a way to specify partial custom types in type annotations?

Is there a way to specify partial custom types in type annotations?
For instance, if I have my type aliases this way: func : { a | foo : String } can I have this function:
when : Bool -> msg -> msg -> msg
when ok msgTrue msgFalse =
if ok then
msgTrue
else
msgFalse
and I would like to do something like this:
when : Bool -> (msg | NoOp) -> msg
when ok msg =
if ok then
msg
else
NoOp
where we know msg has at least NoOp value

In Elm, how can I listen to mousewheel events generated on a node

See the following example on Ellie:
module Main exposing (main)
import Html exposing (..)
import Html.Attributes exposing (style)
import Html.Events exposing (Options, onClick, onWithOptions)
import Json.Decode as JD
import Json.Decode.Extra exposing ((|:))
import Result exposing (withDefault)
import String as S
main : Program Never Model Msg
main =
Html.program
{ init = init
, update = update
, subscriptions = subscriptions
, view = view
}
init : ( Model, Cmd Msg )
init =
( Model "", Cmd.none )
type alias Model =
{ message : String
}
type Msg
= Zoom MouseWheelEvent
| Clicked
type alias MouseWheelEvent =
{ deltaX : Float
, deltaY : Float
, clientX : Float
, clientY : Float
}
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
Zoom e ->
( { model | message = toString e }, Cmd.none )
Clicked ->
( { model | message = "Clicked!" }, Cmd.none )
decodeMouseEvent : JD.Decoder Msg
decodeMouseEvent =
JD.map Zoom
(JD.succeed MouseWheelEvent
|: mouseAttribute "deltaX"
|: mouseAttribute "deltaX"
|: mouseAttribute "deltaX"
|: mouseAttribute "deltaX"
)
mouseAttribute : String -> JD.Decoder Float
mouseAttribute k =
JD.at [ k ] JD.string |> JD.andThen readFloat
readFloat : String -> JD.Decoder Float
readFloat n =
JD.succeed (withDefault 0 (S.toFloat n))
view : Model -> Html Msg
view m =
div []
[ p [] [ text <| "message: " ++ m.message ]
, div
[ onWithOptions "mousewheel" (Options True True) decodeMouseEvent
, onClick Clicked
, style
[ ( "height", "250px" )
, ( "width", "250px" )
, ( "background-color", "green" )
]
]
[ p [] [ text "Zoom here" ]
]
]
subscriptions m =
Sub.none
I want to capture the mousewheel event on a specific element. This is to do with creating a zoom effect on an SVG image.
As per the example, if I attach a listener to a specific element then the update function does not get called. The event appears to be captured by the document body and does not reach my target element. So how do I go about getting my event handler to trigger?
I appreciate it might be possible to do this with ports, but it seems wrong to grab something which is occurring at the document.body level, when I am only interested in capturing it when it occurs over a specific element.
Is it the case that mousewheel is only ever viewed as a document level event?
The event is being triggered but your decoder is failing because the fields event.deltaX, event.deltaY, etc are not strings but floats. AFAIK Elm silently ignores the event if the decoder fails. It works if I change the decoder from:
decodeMouseEvent : JD.Decoder Msg
decodeMouseEvent =
JD.map Zoom
(JD.succeed MouseWheelEvent
|: mouseAttribute "deltaX"
|: mouseAttribute "deltaX"
|: mouseAttribute "deltaX"
|: mouseAttribute "deltaX"
)
mouseAttribute : String -> JD.Decoder Float
mouseAttribute k =
JD.at [ k ] JD.string |> JD.andThen readFloat
readFloat : String -> JD.Decoder Float
readFloat n =
JD.succeed (withDefault 0 (S.toFloat n))
to:
decodeMouseEvent : JD.Decoder Msg
decodeMouseEvent =
JD.map Zoom
(JD.succeed MouseWheelEvent
|: JD.field "deltaX" JD.float
|: JD.field "deltaY" JD.float
|: JD.field "clientX" JD.float
|: JD.field "clientY" JD.float
)

How Can I Transform An Int Into Html

I am writing a small program that handles and displays guitar chords.
But I got stuck: I don't know how to convert an Int value into Html.
My little render function looks like:
renderGuitarString : GuitarString -> Html Msg
renderGuitarString guitarString =
div [ class "string" ] --here I don't know what to do
and:
view : Model -> Html Msg
view model =
div [] (List.map renderGuitarString model.guitarStrings)
just for the complete picture, my types and my model:
type alias GuitarString =
{ number : Int
, frets : List Fret
}
and:
type alias Fret =
{ number : Int
, tone : ( String, Int )
}
and:
type alias Model =
{ guitarStrings : List GuitarString
}
I want to transform the Fret number value into real Html.
thanks for help!
For me, toString did not work and may be outdated. But String.fromInt did.
UPDATE: this will not work in Elm 0.19, use String.fromInt instead
You use Html.text to display a string, but the problem is that an integer is not a string, so you'll have to use toString. For example:
renderGuitarStringNum : Int -> Html Msg
renderGuitarStringNum num =
text (toString num)
You could also render that as
renderGuitarStringNum = toString >> text
Or
renderGuitarStringNum = text << toString