with started lenguaje instalar getting functional-programming elm frp

functional programming - started - ¿Cómo obtengo la hora actual en Elm 0.17/0.18?



instalar elm (6)

Una opción sin tener que buscar el tiempo en cada ruta de actualización sería envolver su Msg en otro tipo de mensaje que recuperaría la hora y luego llamar a su update normal con el tiempo. Esta es una versión modificada de http://elm-lang.org/examples/buttons que actualizará una marca de tiempo en el modelo con cada actualización.

import Html exposing (div, button, text) import Html.App exposing (program) import Html.Events exposing (onClick) import Task import Time exposing (Time) main = program { init = (Model 0 0, Cmd.none), view = view, update = update, subscriptions = (/_ -> Sub.none) } type alias Model = { count: Int , updateTime : Time } view model = Html.App.map GetTimeAndThen (modelView model) type Msg = GetTimeAndThen ModelMsg | GotTime ModelMsg Time update msg model = case msg of GetTimeAndThen wrappedMsg -> (model, Task.perform (/_ -> Debug.crash "") (GotTime wrappedMsg) Time.now) GotTime wrappedMsg time -> let (newModel, cmd) = modelUpdate wrappedMsg time model in (newModel, Cmd.map GetTimeAndThen cmd) type ModelMsg = Increment | Decrement modelUpdate msg time model = case msg of Increment -> ({model | count = model.count + 1, updateTime = time}, Cmd.none) Decrement -> ({model | count = model.count - 1, updateTime = time}, Cmd.none) modelView model = div [] [ button [ onClick Decrement ] [ text "-" ] , div [] [ text (toString model.count) ] , button [ onClick Increment ] [ text "+" ] , div [] [ text (toString model.updateTime) ] ]

Ya había hecho esta pregunta:
¿Cómo obtengo la hora actual en Elm?

Y lo respondí escribiendo mi propia variante ( ahora obsoleta ) de la aplicación de inicio:
http://package.elm-lang.org/packages/z5h/time-app/1.0.1

Por supuesto, la arquitectura Elm ha cambiado desde entonces, y mi vieja forma de hacer las cosas ya no funciona, porque no hay señales o Time.timestamp .

Asi que....

Supongamos que construyo una aplicación con la firma de la función de actualización estándar:
update : Msg -> Model -> (Model, Cmd Msg)

Me gustaría marcar el tiempo de mi modelo con el tiempo en la actualización. Una casi inaceptable solución es suscribirse a Time.every . Conceptualmente esto no es lo que quiero. Esto es actualizar el modelo con el tiempo y también actualizar por separado el modelo con mensajes.

Lo que quiero es poder escribir una función de actualización con firma:
updateWithTime : Msg -> Time -> Model -> (Model, Cmd Msg)

Empecé a tratar de resolver esto agregando algunos mensajes adicionales:
Msg = ... When | NewTime Time

Y creando un nuevo comando:
timeCmd = perform (/x -> NewTime 0.0) NewTime Time.now

Entonces, en cualquier acción, puedo disparar un comando extra para recuperar el tiempo. Pero esto se vuelve desordenado y fuera de control rápidamente.

¿Alguna idea sobre cómo puedo limpiar esto?


He encontrado lo que creo que es una solución más elegante que la respuesta aceptada. En lugar de tener dos modelos separados, el mensaje GetTimeAndThen contiene un controlador que devuelve un mensaje. El código se siente mucho más natural y similar a un olmo, y se puede usar de una manera más general:

module Main exposing (..) import Html exposing (div, button, text) import Html.App as App import Html.Events exposing (onClick) import Task import Time exposing (Time) main = App.program { init = ( Model 0 0, Cmd.none ) , view = view , update = update , subscriptions = (/_ -> Sub.none) } view model = div [] [ button [ onClick decrement ] [ text "-" ] , div [] [ text (toString model) ] , button [ onClick increment ] [ text "+" ] ] increment = GetTimeAndThen (/time -> Increment time) decrement = GetTimeAndThen (/time -> Decrement time) type Msg = Increment Time | Decrement Time | GetTimeAndThen (Time -> Msg) type alias Model = { count : Int, updateTime : Time } update : Msg -> Model -> ( Model, Cmd Msg ) update msg model = case msg of GetTimeAndThen successHandler -> ( model, (Task.perform assertNeverHandler successHandler Time.now) ) Increment time -> ( { model | count = model.count + 1, updateTime = time }, Cmd.none ) Decrement time -> ( { model | count = model.count - 1, updateTime = time }, Cmd.none ) assertNeverHandler : a -> b assertNeverHandler = (/_ -> Debug.crash "This should never happen")


Después de una discusión sobre esta pregunta en Slack, aquí hay una implementación alternativa sin funciones en el Msg . Al igual que con la respuesta aceptada, el modelo solo se actualiza cuando la Task Time.now tiene éxito.

import Html exposing (div, button, text) import Html.App as App import Html.Events exposing (onClick) import Task import Time exposing (Time) main = App.program { init = init , view = view , update = update , subscriptions = (/_ -> Sub.none) } view model = div [] [ button [ onClick Decrement ] [ text "-" ] , div [] [ text (toString model) ] , button [ onClick Increment ] [ text "+" ] ] type Msg = NoOp | Increment | Decrement | GetTimeSuccess Msg Time | GetTimeFailure String type alias Model = { count : Int, updateTime : Result String Time } init : (Model , Cmd Msg) init = ( { count = 0 , updateTime = Err "No time yet!" } , Task.perform GetTimeFailure (GetTimeSuccess NoOp) Time.now ) update : Msg -> Model -> ( Model, Cmd Msg ) update msg model = case msg of NoOp -> (model, Cmd.none) Increment -> ( model , Task.perform GetTimeFailure (GetTimeSuccess Increment) Time.now ) Decrement -> ( model , Task.perform GetTimeFailure (GetTimeSuccess Decrement) Time.now ) GetTimeSuccess Increment time -> ( { model | count = model.count + 1, updateTime = Ok time} , Cmd.none ) GetTimeSuccess Decrement time -> ( { model | count = model.count - 1, updateTime = Ok time} , Cmd.none ) GetTimeSuccess _ time -> ( { model | updateTime = Ok time} , Cmd.none ) GetTimeFailure msg -> ( { model | updateTime = Err msg} , Cmd.none )


Puede crear un módulo nativo y luego exponer una función de timestamp que obtiene el tiempo de Date.now() en JavaScript.

Esto es más o menos lo que se vería así:

Timestamp.elm

module Timestamp exposing (timestamp) import Native.Timestamp timestamp : () -> Int timestamp a = Native.Timestamp.timestamp a

Native / Timestamp.js

var _YourRepoUserName$your_repo$Native_Timestamp = function() { return { timestamp: function(a) {return Date.now()} }

Main.elm

port module Main exposing (..) import Timestamp exposing (timestamp)

luego puede usar ( timestamp () ) en cualquier parte de Elm para obtener la marca de tiempo actual como Int.

Nota: utilicé la timestamp : () -> Int porque no pude hacer que funcione de otra manera. timestamp : Int simplemente devolvió el tiempo codificado de la primera carga.

Avíseme si esto podría mejorarse.


Tengo una respuesta a mi propia pregunta (basada en una sugerencia de amilner42). Estoy usando esta solución en mi código actual.

Me gusta mucho la solución de @ w.brian, pero las funciones en los mensajes rompen el depurador.
Me gusta la solución de @robertjlooby, y esto es muy similar, aunque elimina un tipo adicional y se actualiza para 0.18.

update : Msg -> Model -> ( Model, Cmd Msg ) update msg model = case msg of NoOp -> model ! [] TickThen msg -> model ! [ Task.perform (Tock msg) Time.now ] Tock msg time -> updateTimeStampedModel msg { model | time = time } otherMsg -> update (TickThen msg) model updateTimeStampedModel : Msg -> Model -> ( Model, Cmd Msg ) updateTimeStampedModel msg model = case msg of NoOp -> update msg model TickThen _ -> update msg model Tock _ _ -> update msg model -- ALL OTHER MESSAGES ARE HANDLED HERE, AND ARE CODED TO ASSUME model.time IS UP-TO-DATE.


olmo-0.18 ejemplo completo https://runelm.io/c/72i

import Time exposing (Time) import Html exposing (..) import Html.Events exposing (onClick) import Task type Msg = GetTime | NewTime Time type alias Model = { currentTime : Maybe Time } view : Model -> Html Msg view model = let currentTime = case model.currentTime of Nothing -> text "" Just theTime -> text <| toString theTime in div [] [ button [ onClick GetTime ] [ text "get time" ] , currentTime ] update : Msg -> Model -> ( Model, Cmd Msg ) update msg model = case msg of GetTime -> model ! [ Task.perform NewTime Time.now ] NewTime time -> { model | currentTime = Just time } ! [] main : Program Never Model Msg main = program { init = init , update = update , view = view , subscriptions = always Sub.none } init : ( Model, Cmd Msg ) init = { currentTime = Nothing } ! []