Skip to content

Instantly share code, notes, and snippets.

@localshred
Last active October 12, 2015 15:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save localshred/045835a1f6a8b71950bc to your computer and use it in GitHub Desktop.
Save localshred/045835a1f6a8b71950bc to your computer and use it in GitHub Desktop.
When I comment out line 73 the program compiles. The program isn't complete of course, struggling to figure out where/how this compile error is happening. Don't judge me, just help me.
## 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
Copy link
Author

OK, figured it out. I'm multiplying viewport.rowHeight which is a float, by an int. tl;dr cast pos % rows to float before multiplying with viewport.rowHeight (which is already a float). Notice the (toFloat (pos % rows)) on both lines.

offset : Float -> Int -> Viewport -> Float
offset startOffset pos viewport =
  [
    gutter * 0.5
  , (((toFloat (pos % rows)) - 1) * (toFloat gutter))
  , (viewport.rowHeight * (toFloat (pos % rows)))
  ] |> List.foldr (+) 0

The type signature of (%) is Int -> 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:

1 * 2 -- returns a "number"
1.0 * 2  -- returns a "Float"
1 * 2.0 -- returns a "Float"
1.0 * 2.0 -- returns a "Float"

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:

import Graphics.Element exposing (show)

multFloatInt : Float -> Int -> Float
multFloatInt f i =
--  f * i                Will not compile
  f * (toFloat i)

main = show <| multFloatInt 1.0 2

Oh, and I rewrote offset using let...in instead of List.foldr, because, names matter:

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

@ohanhi
Copy link

ohanhi commented Oct 12, 2015

@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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment