Functional programming 如何获取Elm 0.17/0.18中的当前时间?

Functional programming 如何获取Elm 0.17/0.18中的当前时间?,functional-programming,elm,frp,Functional Programming,Elm,Frp,我已经问过这个问题: 并通过编写我自己的(现在已弃用)启动应用程序变体来回答这个问题: 当然,Elm体系结构已经改变,我以前的做事方式也不再有效,因为没有信号或Time.timestamp 所以 假设我构建了一个具有标准更新函数签名的应用程序: update:Msg->Model->(Model,Cmd-Msg) 我想在我的模型上加上更新时的时间戳。一个几乎不可接受的解决方案是订阅时间。从概念上讲,这不是我想要的。这是随时间更新模型,也是随消息单独更新模型 我想要的是能够编写一个带有签名的更

我已经问过这个问题:

并通过编写我自己的(现在已弃用)启动应用程序变体来回答这个问题:

当然,Elm体系结构已经改变,我以前的做事方式也不再有效,因为没有信号或
Time.timestamp

所以

假设我构建了一个具有标准更新函数签名的应用程序:
update:Msg->Model->(Model,Cmd-Msg)

我想在我的模型上加上更新时的时间戳。一个几乎不可接受的解决方案是订阅
时间。从概念上讲,这不是我想要的。这是随时间更新模型,也是随消息单独更新模型

我想要的是能够编写一个带有签名的更新函数:
updateWithTime:Msg->Time->Model->(Model,Cmd-Msg)


我开始尝试通过添加一些额外的消息来解决这个问题:
Msg=。。。当|新时间

并创建一个新命令:
timeCmd=perform(\x->NewTime 0.0)NewTime Time Time.now

因此,在任何操作中,我都可以发出一个额外的命令来检索时间。但这很快就会变得混乱和失控

有没有关于如何清理这个的想法?

不必在每个更新路径上进行时间提取的一个选项是,将您的
Msg
包装在另一种获取时间的消息类型中,然后使用时间调用您的正常
update
。这是的修改版本,将在每次更新时更新模型上的时间戳

导入Html(div、按钮、文本)
导入Html.App(程序)
导入Html.Events(onClick)
导入任务
导入时间(秒)
主要=
程序{init=(Model 0,Cmd.none),view=view,update=update,subscriptions=(\\\->Sub.none)}
类型别名模型=
{count:Int
,updateTime:Time
}
视图模型=
Html.App.map gettimeanthen(模型视图模型)
输入味精
=GetTime和第三个ModelMsg
|GotTime模型MSG时间
更新msg模型=
味精案例
GetTime和Then WrappedMg->
(模型,Task.perform(\\\->Debug.crash“”)(GotTime wrappedMsg)Time.now)
GotTime wrappedMsg时间->
让
(newModel,cmd)=模型更新wrappedsg时间模型
在里面
(新模型,Cmd.map gettimeanthen Cmd)
类型ModelMsg=增量|减量
模型更新消息时间模型=
味精案例
增量->
({model | count=model.count+1,updateTime=time},Cmd.none)
减量->
({model | count=model.count-1,updateTime=time},Cmd.none)
模型视图模型=
分区[]
[按钮[点击减量][文本“-”]
,div[]文本(toString model.count)]
,按钮[onClick Increment][text“+”]
,div[]文本(toString model.updateTime)]
]

我发现了一个我认为比公认答案更优雅的解决方案。而不是有两个单独的模型,
GetTimeAndThen
消息包含一个返回消息的处理程序。代码感觉更自然,更像榆树,可以以更通用的方式使用:

模块主显示(…)
导入Html(div、按钮、文本)
将Html.App作为应用程序导入
导入Html.Events(onClick)
导入任务
导入时间(秒)
主要=
应用程序
{init=(模型0,Cmd.none)
,视图=视图
,update=update
,订阅=(\ \ \->Sub.none)
}
视图模型=
分区[]
[按钮[点击减量][文本“-”]
,div[]文本(toString模型)]
,按钮[onClick increment][text“+”]
]
增量=
gettimeanthen(\time->Increment time)
减量=
gettimeanthen(\time->递减时间)
输入味精
=增量时间
|递减时间
|获取时间和时间(时间->消息)
类型别名模型=
{count:Int,updateTime:Time}
更新:Msg->Model->(Model,Cmd-Msg)
更新msg模型=
味精案例
GetTime和第二个成功处理程序->
(模型,(Task.performance assertNeverHandler successHandler Time.now))
增量时间->
({model | count=model.count+1,updateTime=time},Cmd.none)
递减时间->
({model | count=model.count-1,updateTime=time},Cmd.none)
资产管理人:a->b
资产管理人=
(\ \ \->Debug.crash“这永远不会发生”)

在对Slack上的这个问题进行讨论之后,下面是一个在
Msg
中没有函数的替代实现。与已接受的答案一样,模型仅在
时间时更新。现在
任务
成功

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
            )

您可以创建一个本机模块,然后在JavaScript中公开一个
timestamp
函数,该函数从
Date.now()获取时间

这大致就是它的样子:

Timestamp.elm
模块时间戳公开(时间戳)
导入本机时间戳
时间戳:()->Int
timestamp a=Native.timestamp.timestamp a
本机/Timestamp.js
var\u yourrepo用户名$your\u repo$Native\u Timestamp=function(){
return{timestamp:function(a){return Date.now()}
}
梅因·埃尔姆
端口模块主暴露(…)
导入时间戳(时间戳)
然后,您可以在Elm中的任何位置使用(
timestamp()
)以Int形式获取当前时间戳


注意:我使用了
timestamp:()->Int
,因为我无法让它以其他方式工作。
timestamp:Int
只返回第一次加载的硬编码时间


让我知道这是否可以改进。

我对自己的问题有一个答案(基于amilner42的建议)。我在当前代码中使用此解决方案

我非常喜欢@w.brian的解决方案,但消息中的函数会破坏调试器。
我喜欢@robertjlooby的解决方案,这是非常相似的,你
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.