Get keyboard event in purescript - purescript

I want to get keydown event in purescript so I used DomEvent.
Here is my code.
main :: Eff (HA.HalogenEffects (console :: CONSOLE, timer :: T.TIMER)) Unit
main = HA.runHalogenAff do
body <- HA.awaitBody
cube <- runUI C.cubes unit body
documenttarget <- liftEff $ window >>= document <#> DHT.htmlDocumentToEventTarget
addEventListener (EventType "keydown") (eventListener test) true (documenttarget)
H.liftEff $ T.setInterval (1000 / frameRate) do
HA.runHalogenAff $ cube.query $ H.action C.Tick
When I try to run this code, I am getting error like this.
documenttarget <- liftEff $ window >>= document <#> DHT.htmlDocumentToEventTarget
Coudn't match type Eff with type Aff
I know aff and eff but I am new to purescript so I am not sure what I have to do to fix this issue.
What can I do?

The block that you pass to halogenRunAff is an Aff block, so every line in it must be an Aff. But liftEff returns an Eff instead. So there is a mismatch.
This is what the compiler is telling you: "can't match Eff with Aff".
To fix this, replace liftEff with liftAff:
documenttarget <- liftAff $ window >>= document <#> DHT.htmlDocumentToEventTarget

Related

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?).

How to convert partial functions to safe(Maybe) functions?

I want it to use library-defined partialfunc more convenient, or write callback with partial pattern-matching.
like this,
partialMaybe :: forall a b. (Partial => a -> b) -> a -> Maybe b
I couldn't find similar in some major libraries.
How to define it? or already defined in libs?
data ABC a = A a | B a | C a
f1 = someHigherOrderFunc $ partialMaybe \(A a) -> someFunc a -- if not 'A', return Nothing.
-- same as
f2 = someHigherOrderFunc $ case _ of A a -> Just $ someFunc a
_ -> Nothing -- requires line break, seems syntax redundant...
using: purescript 0.11.6
Edit:
I did it...
partialMaybe :: forall a b. (Partial => a -> b) -> a -> Maybe b
partialMaybe f a = runPure $ catchException (const $ pure Nothing) (Just <<< unsafePartial f <$> pure a)
this is...umm...very ugly. it's not.
'Failed pattern match' exception is thrown by the purescript.
so I think it should be able to handle by purescript.
Can't do it?
If you want an exception if a case is missed, use Partial. If you want otherwise, use Maybe or Either or another appropriate sum type.
You can catch the exception thrown from a failed pattern match. There is no way for a failed pattern match to not throw an exception.

purescript-halogen: modify state by effectful computation

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

A simple example of rest api in servant or "how to mix monads properly"?

I want to build a simple example of a rest api in servant 0.5:
data MyData = MyData { var1 :: Int, var2 :: String }
app :: Application
app = serve api server
api :: Proxy API
api = Proxy
server :: Server API
server = getItems
getItems :: EitherT ServantErr IO [MyData]
getItems = runEitherT $ do
aa <- nextRandom -- IO
bb <- getCurrentTime -- IO
cc <- getDataFromDb -- IO
--noteT ??? How???
--MaybeT ??? How???
return $ Just [MyData 111 222]
startApp :: IO ()
startApp = run 8080 app
I can't make it compile because of lots errors of "Couldn't match expected type" in different places. I guess it's because I'm mixing 2 different monads in "getItems". But not only.
Here:
getItems :: ExceptT ServantErr IO [MyData]
getItems = runExceptT $ do
What runExceptT does is going from ExceptT ServantErr IO [MyData] to IO (Either ServantErr [MyData]. It eliminates the ExceptT newtype. But you want to go the other way!
You can use liftIO to lift any IO a action into a ExceptT ServantErr IO a action. It basically tells the ExceptT wrapper to "just put the result of the IO action in a success context".
Since your whole do-block seems to live in IO, you could just write:
getItems :: ExceptT ServantErr IO [MyData]
getItems = liftIO $ do
aa <- nextRandom -- IO
bb <- getCurrentTime -- IO
cc <- getDataFromDb -- IO
...
Instead of lifting each IO action separately.
Other common cases:
If you have a pure Either, use hoistEither :: Monad m => Either e a -> ExceptT e m a to lift it into ExceptT.
If you have a pure Maybe, use failWith :: Applicative m => e -> Maybe a -> ExceptT e m a and provide the error.
If you have a IO (Maybe a), use failWithM :: Applicative m => e -> m (Maybe a) -> ExceptT e m a and provide the error.
If you have a IO (Either e a), just wrap it in the ExceptT constructor.
To change the error type carried by an ExceptT, use withExcept :: (e -> e') -> Except e a -> Except e' a.
All these functions are quite simple and looking at their source code is instructive.

Resolving Effects and Maybes

spec = describe "Router" $ do
let sampleRoutes = [( Tuple "/" "views/index.yaml" ),
( Tuple "/foo" "views/foo.yaml" ),
( Tuple "/bar" "views/bar.yaml" )]
it "should default to the first of the list" $ do
r <- fst <$> head sampleRoutes
fprint r
The above throws the following error:
Error in declaration spec
Cannot unify Data.Maybe.Maybe with Control.Monad.Eff.Eff u4505.
I believe its because it is expect a second argument that is of type Eff, but because of
the use of Maybe introduced by head the second arguments ends up being of type Maybe instead.
it :: forall e a. String -> Eff e a -> Eff (it :: It | e) Unit
The problem is, I have no idea how to resolve this. Can I not have a Maybe instead an effectful block of code?
Maybe can be used in a do block, but all of the actions in the block have to be of type Maybe a for some a.
The same is true for Eff eff - you can use Eff eff with do, but all actions have to be of type Eff eff a for some a.
You can't mix and match the two types of effects within a do block.
It looks like you want to use a value of type Maybe a inside a do block whose monad is Eff eff. You have a couple of options:
Use Data.Array.Unsafe.head which will give you an unwrapped Tuple, which you can call fst on directly.
Pattern match on the Maybe value to decide the course of action in the Eff monad:
it "should default to the first of the list" $ do
case head sampleRoutes of
Nothing -> ... -- Handle empty array
Just tuple -> fprint (fst tuple) -- Print the first component
.. rest of do block ..
In this example, it's also possible to make use of traverse_ from Data.Foldable.
Since you're working with a Maybe (Tuple String String), Maybe has a Foldable instance, and Eff e has an applicative instance, you can use traverse_ rather than (<$>).
You just need to supply a function Tuple String String -> Eff e a for some a. If you compose fst and fprint, you get exactly that.
Your example becomes
spec = describe "Router" $ do
let sampleRoutes = [( Tuple "/" "views/index.yaml" ),
( Tuple "/foo" "views/foo.yaml" ),
( Tuple "/bar" "views/bar.yaml" )]
it "should default to the first of the list" $
traverse_ (fst >>> fprint) $ head sampleRoutes