purescript-halogen: modify state by effectful computation - purescript

I want to modify my state with a function that depends on the old state, but also introduces some randomness. My function f looks like this:
f :: State -> Eff (random :: RANDOM) State
I guess my state should be pure, and I had no idea how to get rid off Eff, other than using unsafePerformEff, so I did this:
eval :: Query ~> H.ComponentDSL State Query g
eval (Tick next) = do
H.modify (unsafePerformEff <<< f)
pure next
This works, but there has to be another, more safe way. I already added the random effect to my main function:
main :: Eff (H.HalogenEffects (random :: RANDOM)) Unit
But how should eval look like? Maybe modify does not work here, and there is another way to update state?
Purescript Halogen, side effect (random number) does not work for me, since f depends on the old state.

modify itself doesn't let you perform effectful updates, but yes, you can use get and then modify (or set) afterwards to do so. Adapted from the other example with random:
module Main where
import Prelude
import Control.Monad.Aff (Aff)
import Control.Monad.Eff (Eff)
import Control.Monad.Eff.Random (randomInt, RANDOM)
import Halogen as H
import Halogen.HTML.Events.Indexed as HE
import Halogen.HTML.Indexed as HH
import Halogen.Util (runHalogenAff, awaitBody)
type State = { n :: Int }
initialState :: State
initialState = { n: 3 }
data Query a = NewRandom a
ui :: forall eff. H.Component { n :: Int } Query (Aff (random :: RANDOM | eff))
ui =
H.component { render, eval }
where
render :: State -> H.ComponentHTML Query
render state =
HH.button
[ HE.onClick $ HE.input_ NewRandom ]
[ HH.text $ show state.n ]
eval :: Query ~> H.ComponentDSL State Query (Aff (random :: RANDOM | eff))
eval (NewRandom next) = do
state <- H.get
nextState <- H.fromEff (nextRandom state)
H.set nextState
pure next
nextRandom :: State -> Eff (random :: RANDOM | eff) State
nextRandom { n } = do
nextN <- randomInt (n + 1) (n + 10)
pure { n: nextN }
main :: forall eff. Eff (H.HalogenEffects (random :: RANDOM | eff)) Unit
main =
runHalogenAff do
body <- awaitBody
H.runUI ui initialState body
What could be leading to the type error is this type signature though:
f :: State -> Eff (random :: RANDOM) State
The effect row is closed, meaning it will not unify with any other row, you probably want this instead:
f :: forall eff. State -> Eff (random :: RANDOM | eff) State

Related

Similar record types in a list/array in purescript

Is there any way to do something like
first = {x:0}
second = {x:1,y:1}
both = [first, second]
such that both is inferred as {x::Int | r} or something like that?
I've tried a few things:
[{x:3}] :: Array(forall r. {x::Int|r}) -- nope
test = Nil :: List(forall r. {x::Int|r})
{x:1} : test -- nope
type X r = {x::Int | r}
test = Nil :: List(X) -- nope
test = Nil :: List(X())
{x:1} : test
{x:1, y:1} : test -- nope
Everything I can think of seems to tell me that combining records like this into a collection is not supported. Kind of like, a function can be polymorphic but a list cannot. Is that the correct interpretation? It reminds me a bit of the F# "value restriction" problem, though I thought that was just because of CLR restrictions whereas JS should not have that issue. But maybe it's unrelated.
Is there any way to declare the list/array to support this?
What you're looking for is "existential types", and PureScript just doesn't support those at the syntax level the way Haskell does. But you can roll your own :-)
One way to go is "data abstraction" - i.e. encode the data in terms of operations you'll want to perform on it. For example, let's say you'll want to get the value of x out of them at some point. In that case, make an array of these:
type RecordRep = Unit -> Int
toRecordRep :: forall r. { x :: Int | r } -> RecordRep
toRecordRep {x} _ = x
-- Construct the array using `toRecordRep`
test :: Array RecordRep
test = [ toRecordRep {x:1}, toRecordRep {x:1, y:1} ]
-- Later use the operation
allTheXs :: Array Int
allTheXs = test <#> \r -> r unit
If you have multiple such operations, you can always make a record of them:
type RecordRep =
{ getX :: Unit -> Int
, show :: Unit -> String
, toJavaScript :: Unit -> Foreign.Object
}
toRecordRep r =
{ getX: const r.x
, show: const $ show r.x
, toJavaScript: const $ unsafeCoerce r
}
(note the Unit arguments in every function - they're there for the laziness, assuming each operation could be expensive)
But if you really need the type machinery, you can do what I call "poor man's existential type". If you look closely, existential types are nothing more than "deferred" type checks - deferred to the point where you'll need to see the type. And what's a mechanism to defer something in an ML language? That's right - a function! :-)
newtype RecordRep = RecordRep (forall a. (forall r. {x::Int|r} -> a) -> a)
toRecordRep :: forall r. {x::Int|r} -> RecordRep
toRecordRep r = RecordRep \f -> f r
test :: Array RecordRep
test = [toRecordRep {x:1}, toRecordRep {x:1, y:1}]
allTheXs = test <#> \(RecordRep r) -> r _.x
The way this works is that RecordRep wraps a function, which takes another function, which is polymorphic in r - that is, if you're looking at a RecordRep, you must be prepared to give it a function that can work with any r. toRecordRep wraps the record in such a way that its precise type is not visible on the outside, but it will be used to instantiate the generic function, which you will eventually provide. In my example such function is _.x.
Note, however, that herein lies the problem: the row r is literally not known when you get to work with an element of the array, so you can't do anything with it. Like, at all. All you can do is get the x field, because its existence is hardcoded in the signatures, but besides the x - you just don't know. And that's by design: if you want to put anything into the array, you must be prepared to get anything out of it.
Now, if you do want to do something with the values after all, you'll have to explain that by constraining r, for example:
newtype RecordRep = RecordRep (forall a. (forall r. Show {x::Int|r} => {x::Int|r} -> a) -> a)
toRecordRep :: forall r. Show {x::Int|r} => {x::Int|r} -> RecordRep
toRecordRep r = RecordRep \f -> f r
test :: Array RecordRep
test = [toRecordRep {x:1}, toRecordRep {x:1, y:1}]
showAll = test <#> \(RecordRep r) -> r show
Passing the show function like this works, because we have constrained the row r in such a way that Show {x::Int|r} must exist, and therefore, applying show to {x::Int|r} must work. Repeat for your own type classes as needed.
And here's the interesting part: since type classes are implemented as dictionaries of functions, the two options described above are actually equivalent - in both cases you end up passing around a dictionary of functions, only in the first case it's explicit, but in the second case the compiler does it for you.
Incidentally, this is how Haskell language support for this works as well.
Folloing #FyodorSoikin answer based on "existential types" and what we can find in purescript-exists we can provide yet another solution.
Finally we will be able to build an Array of records which will be "isomorphic" to:
exists tail. Array { x :: Int | tail }
Let's start with type constructor which can be used to existentially quantify over a row type (type of kind #Type). We are not able to use Exists from purescript-exists here because PureScript has no kind polymorphism and original Exists is parameterized over Type.
newtype Exists f = Exists (forall a. f (a :: #Type))
We can follow and reimplement (<Ctrl-c><Ctrl-v> ;-)) definitions from Data.Exists and build a set of tools to work with such Exists values:
module Main where
import Prelude
import Unsafe.Coerce (unsafeCoerce)
import Data.Newtype (class Newtype, unwrap)
newtype Exists f = Exists (forall a. f (a :: #Type))
mkExists :: forall f a. f a -> Exists f
mkExists r = Exists (unsafeCoerce r :: forall a. f a)
runExists :: forall b f. (forall a. f a -> b) -> Exists f -> b
runExists g (Exists f) = g f
Using them we get the ability to build an Array of Records with "any" tail but we have to wrap any such a record type in a newtype before:
newtype R t = R { x :: Int | t }
derive instance newtypeRec :: Newtype (R t) _
Now we can build an Array using mkExists:
arr :: Array (Exists R)
arr = [ mkExists (R { x: 8, y : "test"}), mkExists (R { x: 9, z: 10}) ]
and process values using runExists:
x :: Array [ Int ]
x = map (runExists (unwrap >>> _.x)) arr

aff-ify a function that has different resulting types in error and success callback

Let's first look at a similar function from the web-gl package, for which the intention works:
withShaders
:: forall bindings eff a
. Shaders ({ | bindings }) -> (String -> EffWebGL eff a) -> ({ webGLProgram :: WebGLProg | bindings } -> EffWebGL eff a) -> EffWebGL eff a
makeAff
:: forall e a
. ((Error -> Eff e Unit) -> (a -> Eff e Unit) -> Eff e Unit) -> Aff e a
withShadersAff :: forall eff a. Shaders { | a } -> Aff ( webgl ∷ WebGl | eff ) { webGLProgram ∷ WebGLProg | a }
withShadersAff arg = makeAff (\err ok -> withShaders arg (error >>> err) ok)
This basically turns the callback based withShaders function into one that can be used in aff context.
I'm wondering why, the same thing does not work with the following function (also from the webgl package):
runWebGL :: forall a eff. String -> (String -> Eff eff a) -> (WebGLContext -> EffWebGL eff a) -> Eff eff a
runWebGLAff :: forall eff . String -> Aff ( webgl ∷ WebGl | eff ) WebGLContext
runWebGLAff arg = makeAff (\err ok -> runWebGL arg (error >>> err) ok)
This gives me an 'infinite type error' for the last function. I guess it's because here the error callback and the success callback don't share the same result type, but I could not find away to fix this.
EDIT
After reading the accpeted answer, the solution is:
runWebGLAff arg = makeAff (\err ok -> runWebGL arg (error >>> err) (unsafeCoerceEff <<< ok))
EffWebGL is defined as follows in the library:
type EffWebGL eff a = Eff (webgl :: WebGl | eff) a
It's just an alias for Eff, but notice that its effect row includes the WebGl effect.
When the compiler tries to reconcile this in your function, it deduces, from the usage of ok as callback, that ok :: Eff (webgl | eff) a, but since the ok callback must have the same type as the error callback (from the signature of makeAff), the compiler also deduces that err :: Eff (webgl | eff) a, and therefore, error >>> err :: Eff (webgl | eff) a. However, since error >>> err is used as parameter to runWebGL, it means that error >>> err :: Eff eff a (that's how runWebGL is defined). So the compiler now has two pieces of information that must be true:
(1) error >>> err :: Eff (webgl | eff) a
(2) error >>> err :: Eff eff a
And this, of course, must mean that (webgl | eff) === eff. So eff is part of its own definition. Aka "infinite type". That's why you're getting the error.
Now, in order to fix it, you must take your parameter err as Eff (webgl | eff) a (as required by the type of makeAff), but pass it to runWebGL as Eff eff a (as required by the type of runWebGL) - i.e. the effect row would widen as it flows from the inner callback to the outer. This should be perfectly safe to do, since the inner callback would never use this effect anyway (as indicated by its type), however, the compiler doesn't have a safe way to do this - which might be one of the reasons the effect rows were ditched in PureScript 0.12 (and good riddance!)
Instead, you have to use the type-unsafe way from Control.Monad.Eff.Unsafe:
unsafeCoerceEff :: forall eff1 eff2 a. Eff eff1 a -> Eff eff2 a
Just wrap the error callback in a call to this function, and it will work:
runWebGLAff arg = makeAff (\err ok -> runWebGL arg (unsafeCoerceEff $ error >>> err) ok)
P.S. Normally I would recommend switching to PS 0.12, but it seems you can't do that, because the WebGL library hasn't been updated (yet?).

Type checking error useing random in purescript

With the following code insertRnd works properly but I can't get scramble or anything else that uses insertRnd to compile:
scramble :: ∀ eff. String -> Eff (random :: RANDOM | eff) String
scramble s = foldl insertRnd (take 1 s) (drop 1 s)
insertRnd :: ∀ eff. String -> String -> Eff (random :: RANDOM | eff) String
insertRnd st ch = do
n <- randomInt 0 $ length st
pure $ insertAt n ch st
I get the following error message
Could not match type
Eff
( random :: RANDOM
| t2
)
String
with type
String
while checking that type forall eff.
String
-> String
-> Eff
( random :: RANDOM
| eff
)
String
is at least as general as type t0 -> t1 -> t0
while checking that expression insertRnd
has type t0 -> t1 -> t0
in value declaration scramble
where t0 is an unknown type
t1 is an unknown type
t2 is an unknown type
What am I doing wrong?
First, your insertRnd itself doesn't compile for me, because there is no function insertAt :: Int -> String -> String -> String (or even Int -> Char -> String -> String, see below). There is such function for arrays and lists and some other stuff, but not for strings. I will just assume here that you wrote such function yourself and it exists in your code.
Second, even if it does compile, it has wrong signature for a fold: you're trying to fold over a String, which contains Chars, which means that the folding function's second argument must be a Char. So here I'll assume that your insertRnd actually has the following signature:
insertRnd :: ∀ eff. String -> Char -> Eff (random :: RANDOM | eff) String
Third, you can't actually foldl over a String, because String doesn't have an instance of Foldable. To fold over the characters of a string, you need to first convert the string to an array (or some other container) of Char. Fortunately, there is a helpful function for that - toCharArray.
Fourth, your claim about not being able to compile "anything else that uses insertRnd" is probably too broad. Here's a perfectly compilable example of "anything else" that uses insertRnd:
main = launchAff do
x <- liftEff $ insertRnd "abcd" 'x'
log x
And finally, the reason that your foldl doesn't compile is that foldl expects a pure function a -> b -> a as first argument, but you're giving it an effectful function a -> b -> Eff _ a. No wonder there is a type mismatch!
The effectful analog of foldl is foldM. This function does a similar thing, except it chains the calls within a monad instead of doing pure functional composition. For example:
foldM insertRnd "a" ['b', 'c', 'd']
Applying all of the above to your code, here's the final version (assuming the existence of insertAt :: Int -> Char -> String -> String):
scramble :: ∀ eff. String -> Eff (random :: RANDOM | eff) String
scramble s = foldM insertRnd (take 1 s) (toCharArray $ drop 1 s)
insertRnd :: ∀ eff. String -> Char -> Eff (random :: RANDOM | eff) String
insertRnd st ch = do
n <- randomInt 0 $ length st
pure $ insertAt n ch st

Purescript signal repeatedly starting at beginning of time

I'm building a game in Purescript, using purescript-signal, which includes movement. The user presses the left/right key to move left/right. Minimal code is below.
It looks like purescript is evaluating the signal over "from the beginning of time" every step, which is baffling me. For example, if I keep pressing the right key at the beginning, the output is
m: 0
m: 0
m: 1
m: 0
m: 1
m: 2
m: 0
m: 1
m: 2
m: 3
rather than
m: 0
m: 1
m: 2
m: 3
as I would expect. How do I fix this?
module SimpleMove where
import Prelude
import Control.Monad.Eff (Eff)
import Control.Monad.Eff.Console (CONSOLE, log)
import Data.Functor
import Data.Int
import Signal (Signal, runSignal, foldp, sampleOn, map2)
import Signal.DOM (keyPressed)
import Signal.Time (Time, second, every)
import Partial.Unsafe (unsafePartial)
--MODEL
type Model = Int
step :: forall e. Partial => Int -> Eff (console :: CONSOLE | e) Model -> Eff (console :: CONSOLE| e) Model
step dir m' =
do
m <- m'
log ("m: " <> (show m))
pure (m + dir)
--SIGNALS
inputDir :: Eff _ (Signal Int)
inputDir =
let
f = \l r -> if l
then -1
else if r
then 1
else 0
in
map2 f <$> (keyPressed 37) <*> (keyPressed 39)
input :: Eff _ (Signal Int)
input = sampleOn (every second) <$> inputDir
--MAIN
main :: Eff _ Unit
main =
unsafePartial do
dirSignal <- input
let game = foldp step (pure 0) dirSignal
runSignal (map void game)
If you change your main and step like this you'll get the expected result:
main :: Eff _ Unit
main = do
dirSignal <- input
let game = foldp step 0 dirSignal
runSignal (map render game)
step :: forall e. Int -> Model -> Model
step dir m = m + dir
render :: forall e. Model -> Eff (console :: CONSOLE| e) Unit
render m = logShow m

Simplest possible use of STStrMap.poke in purescript

I'm trying to use the STStrMap purescript module to maintain a map for a long running server application. It's a very simple String map. Here is what I have so far:
import Data.StrMap.ST (new, STStrMap, poke)
import Control.Monad.ST (ST, runST)
type MyMap = forall h e. Eff ( st :: ST h | e) (STStrMap h String)
myMap :: MyMap
myMap = new
-- pokeAString :: String -> String -> MyMap ??
pokeAString k v = poke k v myMap
The signature of MyMap is Eff, but poke expects a STStrMap as the first parameter. I'm not sure how to code this correctly. Note: I'm a newbie to purescript.
The STStrMap you initialize has side effects tracked by the Eff monad, so we have to use its instance of bind to run our computations (e.g. peek, poke), which has the type:
forall e a b. Eff e a -> (a -> Eff e b) -> Eff e b
In psci you can see how this works:
import Prelude
import Data.StrMap.ST
let myMap = new
let myPoke x = x >>= (\m -> poke m "key" "value")
let myPeek x = x >>= (\m -> peek m "key")
myPeek $ myPoke myMap
So your code becomes something like:
import Prelude
import Data.StrMap.ST (new, STStrMap, poke)
import Control.Monad.ST (ST)
import Control.Monad.Eff (Eff)
type MyMap = forall h e. Eff ( st :: ST h | e) (STStrMap h String)
myMap :: MyMap
myMap = new
pokeAString :: String -> String -> MyMap
pokeAString k v = do
a <- myMap
poke a k v