11

好的,我真正想做的是,我有一个数组,我想从中选择一个随机元素。显而易见的事情是从随机数生成器中获取一个介于 0 和长度负 1 之间的整数,我已经在使用它,然后应用Array.get,但它返回一个Maybe a. (似乎还有一个包函数可以做同样的事情。)来自 Haskell,我得到了类型的重要性,它可以保护我免受索引超出范围的情况,但我可以控制索引并且不预计会发生这种情况,所以我只想假设我得到了Just一些东西并且有点强行转换为a. 在 Haskell 中,这将是fromJust,或者,如果我觉得冗长的话,fromMaybe (error "some message").我应该如何在 Elm 中做到这一点?

在邮件列表上发现了一个似乎正在讨论这个问题的讨论,但已经有一段时间了,我在标准库中没有看到我想要的功能,讨论表明它会是。

以下是我迄今为止发现的一些非常不令人满意的潜在解决方案:

  • 只需使用withDefault。我确实有一个a可用的默认值,但我不喜欢这样,因为它给我的代码带来了完全错误的含义,并且可能会使调试变得更加困难。
  • 对端口进行一些摆弄以与 Javascript 交互,如果没有,则在此处抛出异常。我还没有仔细研究它是如何工作的,但显然这是可能的。但这似乎只是混合了太多的依赖关系,否则就是简单的纯 Elm。
4

2 回答 2

6

(回答我自己的问题)

我发现了两个更令人满意的解决方案:

  • 滚动我自己的部分定义函数,该函数在链接讨论的其他地方被引用。但是这样的代码感觉不完整(我希望编译器有一天会警告我不完整的模式匹配)并且错误消息仍然不清楚。
  • 模式匹配并使用Debug.crash如果它是空的话。这看起来类似于 Haskell 的error,是我现在倾向于的解决方案。

    import Debug
    
    fromJust : Maybe a -> a
    fromJust x = case x of
        Just y -> y
        Nothing -> Debug.crash "error: fromJust Nothing"
    

    (不过,模块名称和描述也让我犹豫,因为它看起来不像是为我的目的而设计的“正确”方法;我想指出真正的程序员错误,而不是单纯的调试。)

于 2015-02-24T15:30:43.737 回答
3

解决方案

一个或等效函数的存在或使用fromJust实际上是代码异味,并告诉您该 API 设计不正确。问题是你试图在获得信息之前就决定要做什么。您可以在两种情况下考虑这一点:

  1. 如果你知道你应该做什么Nothing,那么解决方案很简单:使用withDefault. 当您查看代码中的正确点时,这将变得显而易见。

  2. 如果你不知道在你拥有的情况下应该做什么Nothing,但你仍然想做出改变,那么你需要一种不同的方式来做到这一点。而不是MaybeMaybe.map保留Maybe. 例如,假设您正在执行以下操作:

    foo : Maybe Int -> Int
    foo maybeVal =
      let
        innerVal = fromJust maybeVal
      in
        innerVal + 2
    

    相反,你会想要这个:

    foo : Maybe Int -> Maybe Int
    foo maybeVal =
        Maybe.map (\innerVal -> innerVal + 2) maybeVal
    

    请注意,您想要的更改在这种情况下仍然完成,您根本没有处理您拥有Nothing. 您现在可以在调用链上上下传递这个值,直到找到一个可以自然地withDefault用来摆脱Maybe.

发生的情况是,我们将“如何更改此值”和“当它不存在时我该怎么办?”的关注点分开。我们用 处理前者Maybe.map,用 处理后者Maybe.withDefault

警告

在少数情况下,您只是知道自己有一个Just价值并需要fromJust按照您的描述使用它来消除它,但这些情况应该很少见。有很多实际上有一个更简单的选择。

示例:尝试过滤列表并获取值。

假设您有一个Maybe想要其值的 s 列表。一个常见的策略可能是:

foo : List (Maybe a) -> List a
foo hasAnything =
  let
    onlyHasJustValues = List.filter Maybe.isJust hasAnything
    onlyHasRealValues = List.map fromJust onlyHasJustValues
  in
    onlyHasRealValues

事实证明,即使在这种情况下,也有一些干净的方法可以避免fromJust. 大多数具有包含映射和过滤器的集合的语言都有一种使用Maybe内置过滤器的方法。Haskell 有Maybe.mapMaybe,Scala 有flatMap,Elm 有List.filterMap。这会将您的代码转换为:

foo : List (Maybe a) -> List a
foo hasAnything =
  let
    onlyHasRealValues = List.filterMap (\x -> x) hasAnything
  in
    onlyHasRealValues
于 2017-01-15T00:59:25.410 回答