-
-
Save localshred/045835a1f6a8b71950bc to your computer and use it in GitHub Desktop.
## ERRORS in ../tic-tac-toe/TicTacToe.elm ###################################### | |
-- TYPE MISMATCH ---------------------------------- ../tic-tac-toe/TicTacToe.elm | |
The type annotation for `offset` does not match its definition. | |
68| offset : Float -> Int -> Viewport -> Float | |
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
As I infer the type of values flowing through your program, I see a conflict | |
between these two types: | |
Float | |
Int | |
Detected errors in 1 module. |
module TicTacToe where | |
import Graphics.Element exposing (Element, centered) | |
import Graphics.Collage exposing (Form, toForm, collage, moveY, move, outlined, dashed, square) | |
import Color exposing (yellow, gray, blue, green, orange, red) | |
import Text exposing (fromString) | |
import Window | |
import Signal exposing ((<~)) | |
import Array exposing (initialize, repeat, toList) | |
gutter = 10 | |
rows = 3 | |
cols = 3 | |
type alias Viewport = | |
{ width : Int | |
, height : Int | |
, halfWidth : Int | |
, halfHeight : Int | |
, minX : Float | |
, maxX : Float | |
, minY : Float | |
, maxY : Float | |
, rowHeight : Float | |
} | |
main : Signal Element | |
main = | |
drawBoard <~ Window.dimensions | |
makeViewport : (Int,Int) -> Viewport | |
makeViewport (width,height) = | |
let | |
halfWidth = width // 2 | |
halfHeight = height // 2 | |
minX = negate halfWidth |> toFloat | |
maxX = halfWidth |> toFloat | |
minY = negate halfHeight |> toFloat | |
maxY = halfHeight |> toFloat | |
rowHeight = ((height // rows) - ((rows - 1) * gutter)) |> toFloat | |
in | |
Viewport width height halfWidth halfHeight minX maxX minY maxY rowHeight | |
drawBoard : (Int,Int) -> Element | |
drawBoard dimensions = | |
let | |
viewport = makeViewport dimensions | |
gameRows = makeRows rows cols viewport | |
title = "Tic-Tac-Toe" |> fromString |> centered |> toForm |> moveY (negate (viewport.minY + 30)) | |
in | |
collage viewport.width viewport.height <| title :: gameRows | |
makeRows : Int -> Int -> Viewport -> List Form | |
makeRows rows cols viewport = | |
[ makeSquare 0 viewport | |
, makeSquare 1 viewport | |
, makeSquare 2 viewport | |
, makeSquare 3 viewport | |
, makeSquare 4 viewport | |
, makeSquare 5 viewport | |
, makeSquare 6 viewport | |
, makeSquare 7 viewport | |
, makeSquare 8 viewport | |
] | |
offset : Float -> Int -> Viewport -> Float | |
offset startOffset pos viewport = | |
[ | |
((toFloat gutter) / 2) | |
, (((pos % rows) - 1) * gutter) |> toFloat | |
, (viewport.rowHeight * (pos % rows)) |> toFloat -- <<<<< If I comment out this line, the program compiles. WUT? | |
] |> List.foldr (+) 0 | |
moveSquare : Int -> Viewport -> Form -> Form | |
moveSquare pos viewport form = | |
let | |
x = offset viewport.minX pos viewport | |
y = offset viewport.minY pos viewport | |
in | |
move (x,y) form | |
makeSquare : Int -> Viewport -> Form | |
makeSquare pos viewport = | |
moveSquare pos viewport <| outlined (dashed blue) (square viewport.rowHeight) |
module TicTacToe where | |
import Graphics.Element exposing (Element, centered) | |
import Graphics.Collage exposing (Form, toForm, collage, moveY, move, outlined, dashed, square) | |
import Color exposing (yellow, gray, blue, green, orange, red) | |
import Text exposing (fromString) | |
import Window | |
import Signal exposing ((<~)) | |
import Array exposing (initialize, repeat, toList) | |
gutter = 10 | |
rows = 3 | |
cols = 3 | |
type alias Viewport = | |
{ width : Int | |
, height : Int | |
, halfWidth : Int | |
, halfHeight : Int | |
, minX : Float | |
, maxX : Float | |
, minY : Float | |
, maxY : Float | |
, rowHeight : Float | |
} | |
main : Signal Element | |
main = | |
drawBoard <~ Window.dimensions | |
makeViewport : (Int,Int) -> Viewport | |
makeViewport (width,height) = | |
let | |
halfWidth = width // 2 | |
halfHeight = height // 2 | |
minX = negate halfWidth |> toFloat | |
maxX = halfWidth |> toFloat | |
minY = negate halfHeight |> toFloat | |
maxY = halfHeight |> toFloat | |
rowHeight = ((height // rows) - ((rows - 1) * gutter)) |> toFloat | |
in | |
Viewport width height halfWidth halfHeight minX maxX minY maxY rowHeight | |
drawBoard : (Int,Int) -> Element | |
drawBoard dimensions = | |
let | |
viewport = makeViewport dimensions | |
gameRows = makeRows rows cols viewport | |
title = "Tic-Tac-Toe" |> fromString |> centered |> toForm |> moveY (negate (viewport.minY + 30)) | |
in | |
collage viewport.width viewport.height <| title :: gameRows | |
makeRows : Int -> Int -> Viewport -> List Form | |
makeRows rows cols viewport = | |
[ makeSquare 0 viewport | |
, makeSquare 1 viewport | |
, makeSquare 2 viewport | |
, makeSquare 3 viewport | |
, makeSquare 4 viewport | |
, makeSquare 5 viewport | |
, makeSquare 6 viewport | |
, makeSquare 7 viewport | |
, makeSquare 8 viewport | |
] | |
offset : Float -> Int -> Viewport -> Float | |
offset startOffset pos viewport = | |
let | |
halfGutter = gutter * 0.5 | |
gutterWidths = (((toFloat (pos % rows)) - 1) * (toFloat gutter)) | |
squareWidths = (viewport.rowHeight * (toFloat (pos % rows))) | |
in | |
halfGutter + gutterWidths + squareWidths | |
squarePosition : Int -> Viewport -> (Float,Float) | |
squarePosition pos viewport = | |
let | |
x = offset viewport.minX pos viewport | |
y = offset viewport.minY pos viewport | |
in | |
(,) x y | |
makeSquare : Int -> Viewport -> Form | |
makeSquare pos viewport = | |
let | |
coordinates = squarePosition pos viewport | |
in | |
outlined (dashed blue) (square viewport.rowHeight) | |
|> move coordinates | |
@localshred Good that you found the culprit! The Int
vs. Float
vs. number
thing is a bit confusing in its own right. Especially since you can't use number
in the type annotations, so it's only in use with unannotated functions.
It's true the compiler sometimes stumbles and just says "the function does not match its definition". I have found it a good practice at this point to promote functions from the let
block to standalone, type annotated functions. This way the compiler will be much more helpful.
Another thing, you say:
when enclosing the multiplication inside of a function, and giving the function a type signature, the auto-coercion appears to go away
Strictly speaking, this is not true. What happens instead is that the type inference comes to the conclusion that the function works only for a certain combination of types, for example number
and Int
. However, the annotated type signature does not fit and thus it results in an error. So in short, the type inference system uses the type annotation only to verify whether the inferred types match those that the programmer wanted to have.
I hope this was any help. Good luck on your future endeavors with Elm!
PS. In case you're interested, I wrote a piece when I was learning my first bits of Elm: https://gist.github.com/ohanhi/0d3d83cf3f0d7bbea9db
OK, figured it out. I'm multiplying
viewport.rowHeight
which is a float, by an int. tl;dr castpos % rows
to float before multiplying withviewport.rowHeight
(which is already a float). Notice the(toFloat (pos % rows))
on both lines.The type signature of
(%)
isInt -> Int -> Int
. I can't tell if the REPL is doing nice things for me in coercing numbers automatically, but here's what I see:In the REPL the
(*)
function handles all of the cases, but when enclosing the multiplication inside of a function, and giving the function a type signature, the auto-coercion appears to go away. My confusion is simply that the type-checker told me the whole function signature was wrong (even though I thought I was casting correctly), and didn't indicate which line (or which argument to(*)
was incorrectly typed.This simple program illustrates this issue, I think:
Oh, and I rewrote
offset
usinglet...in
instead ofList.foldr
, because, names matter: