【问题标题】:Is let-in lazy in its bindings in Elm?let-in 在 Elm 中的绑定是惰性的吗?
【发布时间】:2019-03-14 17:46:58
【问题描述】:

我有两个功能 A 和 B,可以同时禁用、启用 A、启用 B,但不能同时启用。看完Making Impossible States Impossible 之后,我想尝试在类型级别上强制执行此操作。

我正在考虑的解决方案的简化版本如下。

module Main exposing (main)

import Browser
import Html exposing (Html, button, div, text)
import Html.Events exposing (onClick)

type Model
  = NoneEnabled
  | AEnabled
  | BEnabled

init : Model
init = NoneEnabled

type Msg
  = EnableA
  | DisableA
  | EnableB
  | DisableB

view : Model -> Html Msg
view model =
  let -- Buttons to enable and disable features
      buttons =
        div [] [ button [onClick EnableA] [text "Enable A"]
               , button [onClick DisableA] [text "Disable A"]
               , button [onClick EnableB] [text "Enable B"]
               , button [onClick DisableB] [text "Disable B"]
               ]

      -- All possible feature states
      aEnabled  = div [] [text "A enabled"]
      aDisabled = div [] [text "A disabled"]
      bEnabled  = div [] [text "B enabled"]
      bDisabled = div [] [text "B disabled"]
  in case model of
       NoneEnabled ->
         div [] [buttons, aDisabled, bDisabled]
       AEnabled ->
         div [] [buttons, aEnabled, bDisabled]
       BEnabled ->
         div [] [buttons, aDisabled, bEnabled]

update : Msg -> Model -> Model
update msg model =
  case (msg, model) of
    (EnableA, _) ->
      AEnabled
    (EnableB, _) ->
      BEnabled
    (DisableA, AEnabled) ->
      NoneEnabled
    (DisableB, BEnabled) ->
      NoneEnabled
    _ ->
      model

main : Program () Model Msg
main =
  Browser.sandbox { init = init, update = update, view = view }

view 中的 aEnabledaDisabledbEnabledbDisabled 功能的计算成本可能很高。无论case model of 采用哪个分支,它们都会被评估,还是我可以只依赖正在评估的已使用功能?

或者用更短的例子来表达。

f c x =
  let a = x + 1
      b = x + 2
  in case c of
       True ->
         a
       False ->
         b

f True 0 会在 let 表达式中强制对 b 求值吗?

【问题讨论】:

    标签: lazy-evaluation elm let


    【解决方案1】:

    Elm 的 let/in 语句不会被延迟评估。你可以放一些Debug.log 语句来证明这一点:

    f c x =
      let a = Debug.log "a calculated" <| x + 1
          b = Debug.log "b calculated" <| x + 2
      in case c of
           True ->
             a
           False ->
             b
    

    只调用一次f 会将两条消息都记录到控制台,无论输入如何。 Example here.

    绕过这个障碍的一种方法是要求ab 的任意参数,例如单位()

    f c x =
      let a () = Debug.log "a calculated" <| x + 1
          b () = Debug.log "b calculated" <| x + 2
      in case c of
           True ->
             a ()
           False ->
             b ()
    

    这种变化只会评估函数a b

    【讨论】:

    • 如果我创建 ab 函数,如果在体内使用两次,它们会被评估两次还是 Elm 会“去重复”?
    • 没有记忆,所以这个函数实际上会被调用两次。 See this example,当你调用a () + a ()时,它会在控制台打印两次
    猜你喜欢
    • 1970-01-01
    • 2017-10-28
    • 2014-07-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-31
    • 2021-09-13
    • 1970-01-01
    相关资源
    最近更新 更多