Reputation: 169
I'm trying to use foldp with the result of the input function that combines two signals.
Here is the code I have.
import Graphics.Element (..)
import Graphics.Collage (..)
import Color (..)
import Signal
import Keyboard
import Text (asText)
import Time (..)
-- Display
render : (Int, Int) -> Element
render (xDiff, yDiff) = collage 200 200 [
rotate (degrees (toFloat yDiff))
(filled blue (ngon 5 (10 * toFloat xDiff))) ]
-- Combine two signals to be a pair
input = Signal.map2 (,) (fps 25) Keyboard.arrows
-- Fold past combined signals from input and pass resulting signal to render
main : Signal Element
main = Signal.map render
(Signal.foldp (\dir (upd, {x, y}) ->
(x + dir.x, y + dir.y)) (0,0) input)
-- Fold past Keyboard.arrows and pass resulting signal to render
--main : Signal Element
--main = Signal.map render
-- (Signal.foldp (\dir (x, y) ->
-- (x + dir.x, y + dir.y)) (0,0) Keyboard.arrows)
And the error I get:
Type mismatch between the following types on line 34, column 55 to 60:
(Float, { x : Int, y : Int })
{ a | x : Int, y : number }
It is related to the following expression:
input
Type mismatch between the following types on line 34, column 26 to 46:
{ a | x : Int, y : number }
number
Looks like something besides an Int or Float is being used as a number.
It is related to the following expression:
(x + dir.x,y + dir.y)
Type mismatch between the following types on line 34, column 26 to 46:
Int
{ a | x : number, y : number }
It is related to the following expression:
(x + dir.x,y + dir.y)`
In main I can replace render with asText and get a similar error so I think that even though there might be issues with render handling input I think there is also a problem with the function I am using with foldp.
Upvotes: 4
Views: 411
Reputation: 31
Even with the excellent answer of Apanatshka I was confused by the swapping of arguments when the update function is called from foldp
, so here's a minor write-up to clarify:
module Foldp2 where
import Graphics.Element (..)
import Graphics.Collage (..)
import Color (..)
import Signal
import Keyboard
import Text (asText)
import Time (..)
-- Display
render : (Int, Int) -> Element
render (xDiff, yDiff) = collage 200 200 [
rotate (degrees (toFloat yDiff))
(filled blue (ngon 5 (10 * toFloat xDiff))) ]
-- `input` produces a (Float, Record)-Tuple wrapped in a Signal
input : Signal (Float, {x : Int, y : Int})
input = Signal.map2 (,) (fps 25) Keyboard.arrows
-- define an update function operating on the unwrapped `input` function
-- `foldp` will make it work on the wrapped `input'
update : ( Float, {x:Int, y:Int}) -> (Int, Int) -> (Int, Int)
update (upd, dir) (x,y) = (x + dir.x, y + dir.y)
-- Making the type of 'foldp' applied to 'update' explicit:
-- Notice that the order of arguments compared to `update` is swapped.
-- The swapping, I think, was the origin of confusion for OP (mine indeed it was).
folding : (Int, Int) -> Signal (Float, {x:Int, y:Int}) -> Signal (Int, Int)
folding = Signal.foldp update
-- Further notice that "aligning" the arguments in `update` with those of
-- `folding` by:
-- update : (Int, Int) -> ( Float, {x:Int, y:Int}) -> (Int, Int)
-- update (x,y) (upd, dir) = (x + dir.x, y + dir.y)
-- gives error:
-- Type mismatch between the following types on line 26, column 28 to 48:
-- { x : Int, y : Int }
-- number
-- It is related to the following expression:
-- (x + dir.x,y + dir.y)
-- unless you `flip` the order arguments in `folding`:
-- folding : (Int, Int) -> Signal (Float, {x:Int, y:Int}) -> Signal (Int, Int)
-- folding = Signal.foldp (flip update)
main : Signal Element
main = Signal.map render (folding (0,0) input)
Upvotes: 1
Reputation: 5958
Here's the correct code for main
:
main : Signal Element
main = Signal.map render
(Signal.foldp (\(upd, dir) (x, y) ->
(x + dir.x, y + dir.y)) (0,0) input)
There is a problem with your types. So let's looks at some types:
foldp : (a -> b -> b) -> b -> Signal a -> Signal b
input : Signal (Float, {x : Int, y : Int})
render : (Int, Int) -> Element
main : Signal Element
Within the definition of main
the problem seems to originate from foldp
, so let's make the type of the foldp
more specific within the context of main
. render
is mapped over the output, so b
should be (Int, Int)
, which corresponds with the (0,0)
initial value. a
should be like the input. So for the specific foldp
in main you have:
foldp : ((Float, {x : Int, y : Int}) -> (Int, Int) -> (Int, Int)) -- the function
-> (Int, Int) -- the initial value
-> Signal (Float, {x : Int, y : Int}) -- the input
-> Signal (Int, Int) -- the output
Ok, so we know what type the function argument of foldp
should have. But what wrong type does the current function have?
(\dir (upd, {x, y}) -> (x + dir.x, y + dir.y)) : {recordExt1 | x : number, y : number'} -> (something, {recordExt2 | x : number, y : number'}) -> (number, number')
Hmm.. That looks too complicated. Let's simplify with some assumptions.
* We probably don't need extended records so take recordExt1 = recordExt2 = {}
.
* The two number
s are likely Int
s.
* The something is the first in a tuple with a record of x
and y
, so that must be the Float
from the input
signal.
-- simplified type of inline function
{ x : Int, y : Int} -> (Float, { x : Int, y : Int}) -> (Int, Int)
-- function deduced from specialised `foldp` type
(Float, {x : Int, y : Int}) -> (Int, Int) -> (Int, Int)
Those look similar. The first and second argument to the function are swapped, and the second argument should be a tuple of integers instead of a record with integers x
and y
.
You can see how the other definition of main
in comments was working, but then you changed the (x,y)
to {x,y}
and added the (upd,
...)
around the wrong argument.
If you're not actually planning on using the time deltas from fps 25
, you can also use:
input = Signal.sampleOn (fps 25) Keyboard.arrows
main = Signal.map render
(Signal.foldp (\dir (x, y) ->
(x + dir.x, y + dir.y)) (0,0) input)
This gives you almost the same behaviour.
Upvotes: 3