Elm 中神秘的类型不匹配

Mysterious Type Mismatch in Elm

下面的代码应该产生一个按钮,当按下它时会创建一个框(以及一个用于移除框的按钮)

import Graphics.Input
import Signal (..)
import Signal
import Mouse
import Text (Text, asText, plainText)
import Graphics.Element (..)
import Graphics.Collage (..)
import Color (..)
import List
import Dict

--                  x    y   id   |         id
type Event = Add (Float, Float, Int) | Remove (Int) | Maybe

makebox : Int -> Element
makebox id =
  let (w, h) = (30, 30)
  in flow down
    [ layers [plainText "aaaa", collage w h [square 30 |> filled red]]
    , button_remove id ]

add = Signal.channel (0,0,0)
remove = Signal.channel (0)

button_add x y = Graphics.Input.button (Signal.send add (x, y, 2)) "add a box"

button_remove id = Graphics.Input.button (Signal.send remove (id)) "remove me"

main =
  let update event =
    case event of
      Add (x, y, id)  ->  Dict.insert id ((x,y), ((makebox id)))
      Remove (id)     ->  Dict.remove id
      Maybe          ->  identity
  in Signal.map (\dict ->  flow down
                            [ button_add 10.0 20.0 --makes add & remove buttons
                            , collage 500 500 (List.map (\(Just ((x,y), makebox)) -> move (x,y) makebox)
                                                (Dict.values dict)) --draws the dict
                            ]) --map function argument
  (foldp update Dict.empty
    (merge
      (Add    <~ (Signal.subscribe add)) --pipes button channels into events
      (Remove <~ (Signal.subscribe remove)))) --map signal argument

但是,它会产生这种类型的错误:

Type mismatch between the following types on line 40, column 14 to 20:

   ((Int, Int))

   Maybe.Maybe

It is related to the following expression:

   update

我看不出这个错误是从哪里来的,Maybe.Maybe 是从哪里传递到 update 的,我该如何解决它?

TL;DR

查找并替换 maincollage 调用的行:

                            , collage 500 500 (List.map (\((x,y), makebox) -> move (x,y) (toForm makebox))

快速代码清理

代码太多了。我不得不稍微改变一下风格来理解它是如何工作的。我所做的是将更多功能移至顶层并为它们提供类型注释。我还将 button_add/remove 更改为驼峰式,因为这是标准的 Elm 命名约定。这是结果:

import Graphics.Input
import Signal (..)
import Signal
import Mouse
import Text (Text, asText, plainText)
import Graphics.Element (..)
import Graphics.Collage (..)
import Color (..)
import List
import Dict

--                  x    y   id   |         id
type Event = Add (Float, Float, Int) | Remove (Int) | Maybe

type alias Model = Dict.Dict Int ((Float,Float), Element)

makebox : Int -> Element
makebox id =
  let (w, h) = (30, 30)
  in flow down
    [ layers [plainText "aaaa", collage w h [square 30 |> filled red]]
    , buttonRemove id ]

add : Signal.Channel (Float,Float,Int)
add = Signal.channel (0,0,0)

remove : Signal.Channel Int
remove = Signal.channel 0

buttonAdd : Float -> Float -> Element
buttonAdd x y = Graphics.Input.button (Signal.send add (x, y, 2)) "add a box"

buttonRemove : Int -> Element
buttonRemove id = Graphics.Input.button (Signal.send remove id) "remove me"

update : Event -> Model -> Model
update event =
  case event of
    Add (x, y, id)  ->  Dict.insert id ((x,y), makebox id)
    Remove (id)     ->  Dict.remove id
    Maybe           ->  identity

-- move' : Just ((Float,Float),Element) -> Form
move' (Just ((x,y), makebox)) = move (x,y) makebox

view : Model -> Element
view dict = 
  flow down
    [ buttonAdd 10.0 20.0 --makes add & remove buttons
    , collage 500 500 (List.map move' (Dict.values dict)) --draws the dict
    ]

input : Signal Event
input = merge
  (Add    <~ (Signal.subscribe add)) --pipes button channels into events
  (Remove <~ (Signal.subscribe remove))

model : Signal Model
model = foldp update Dict.empty input

main : Signal Element
main = view <~ model

我们仍然得到相同的,不是很有帮助的类型错误。但现在它在 move' 函数上。我在评论中添加了我想到的类型签名。

问题

这段代码有两个问题:

  1. move' 接受一个 Element(除其他外)并尝试 move 它,但 moveForm 起作用。所以这需要调用 toForm,然后才能工作。
  2. 这是类型错误的来源:Dict.values 给出了值列表,而不是 Just 值列表(Maybe.Maybe 类型)。

因此,解决方案是一个 move' 函数,如下所示:

move' : ((Float,Float), Element) -> Form
move' (a,b) = move a (toForm b)