Why does using try in conjuntion with affjax's XHRError result in a mismatch on the Error type? - purescript

I'd like to be able to catch communications errors when using affjax in purescript. I try it like this:
module Main where
import Prelude
import Affjax as AX
import Affjax.ResponseFormat as ResponseFormat
import Data.Argonaut.Core (stringify, fromString)
import Data.Either (Either(..))
import Data.HTTP.Method (Method(..))
import Effect.Aff (launchAff)
import Effect.Class.Console (log)
import Effect.Exception(message)
import Control.Monad.Error.Class (try)
main = void $ launchAff $ do
resultEi <- try $ AX.get ResponseFormat.string "/api"
let result = case resultEi of
Left err -> Left $ AX.XHRError err
rightRes#(Right _)-> rightRes
case result of
Left err -> log $ "GET /api response failed to decode: " <> AX.printError err
Right response -> log $ "GET /api response: " <> response.body
This results in the somewhat confusing error, particularly so because I'm not really sure what would be here other than Effect.Exception.Error since both Aff and XHRError seem to use this Error datatype.
Error 1 of 1
Could not match type
Error
with type
Error
while trying to match type Either Error
with type Either Error
while checking that expression rightRes
has type t0
in value declaration main
where t0 is an unknown type

The branches of your case have different types.
The Left branch has type Either Effect.Exception.Error a, because that's how you construct it.
But your Right branch has the same type as rightRes, because that's what you're returning from it, and that type is Either Affjax.Error (Response _).
So the types don't match.
To make them match, you have to unwrap rightRes and rewrap it again:
let result = case resultEi of
Left err -> Left $ AX.XHRError err
Right res -> Right res
Yes, it will have the exact same content, but the type will be different.
But if your intent is just to change the type of the error, then I'd recommend lmap from Bifunctor:
let result = lmap AX.XHRError resultEi

Related

Purescript cannot get keycode from keyboard

I am new to functional programming and to Purescript. I am trying to get keycode from keys pressed from keyboard. I have made a eventListener which fires when keydown event is fired and triggers event listener function test. I am having problem in converting event to keyboardevent and getting keycode from keyboardevent. I am attaching my code and error it producing.
import Control.Monad.Eff (Eff)
import Control.Monad.Eff.Class
import Control.Monad.Eff.Console (CONSOLE, log)
import DOM (DOM)
import DOM.Event.EventTarget (addEventListener, eventListener)
import DOM.HTML.Types as DHT
import DOM.Event.KeyboardEvent as KE
import DOM.Event.Types (EventType(..), EventTarget)
import DOM.Event.Event
import DOM.HTML (window)
import DOM.HTML.Window (document)
import Prelude (Unit)
test :: forall e. Event -> Eff ( console :: CONSOLE, dom :: DOM | e) Unit
test a = do
ke <- KE.eventToKeyboardEvent a
co <- KE.key ke
log "Key Pressed : "
main :: forall e. Eff (console :: CONSOLE, dom :: DOM | e) Unit
main = do
documenttarget <- liftEff $ window >>= document <#> DHT.htmlDocumentToEventTarget
addEventListener (EventType "keydown") (eventListener test) true (documenttarget)
Error :
Error found:
in module Main
at src/Main.purs line 30, column 10 - line 30, column 19
Could not match type
String
with type
t0 t1
while checking that type String
is at least as general as type t0 t1
while checking that expression key ke
has type t0 t1
in value declaration test
where t0 is an unknown type
t1 is an unknown type
See https://github.com/purescript/documentation/blob/master/errors/TypesDoNotUnify.md for more information,
or to contribute content related to this error.
* ERROR: Subcommand terminated with exit code 1
Your test function needs tweaking a bit:
import Prelude
import Control.Monad.Eff (Eff)
import Control.Monad.Eff.Class (liftEff)
import Control.Monad.Eff.Console (CONSOLE, log)
import Control.Monad.Except (runExcept)
import DOM (DOM)
import DOM.Event.Event (Event)
import DOM.Event.EventTarget (addEventListener, eventListener)
import DOM.Event.KeyboardEvent as KE
import DOM.Event.Types (EventType(..))
import DOM.HTML (window)
import DOM.HTML.Types as DHT
import DOM.HTML.Window (document)
import Data.Either (Either(..))
test :: forall e. Event -> Eff ( console :: CONSOLE, dom :: DOM | e) Unit
test a =
case runExcept (KE.eventToKeyboardEvent a) of
Left err ->
log "Event was not a keyboard event"
Right ke -> do
let co = KE.key ke
log "Key Pressed : "
main :: forall e. Eff (console :: CONSOLE, dom :: DOM | e) Unit
main = do
documenttarget <- liftEff $ window >>= document <#> DHT.htmlDocumentToEventTarget
addEventListener (EventType "keydown") (eventListener test) true (documenttarget)
First, KE.key and KE.eventToKeyboardEvent are not effectful functions, which means you don't need to bind to get the values from them (this is what the t0 t1 error was about: it expects the resulting value to be like m String rather than just String, for some m).
Next, when we use eventToKeyboardEvent, it can fail, since we have no way of guaranteeing that it's safe to coerce any Event to a KeyboardEvent. The result is wrapped in F, which is a synonym for Except MultipleErrors, so we need to do something to get the result of that. We "run" the Except wrapper that gives us an Either back, where the Left side is the error message and Right is the success result.
In this example I made it log when the cast fails, but you probably don't care about that, and generally don't even expect that case to be hit, in which case using pure unit is probably good enough instead.
There's actually another way of writing this if you're happy to let the failure case do nothing:
import Data.Foldable (for_)
test :: forall e. Event -> Eff ( console :: CONSOLE, dom :: DOM | e) Unit
test a =
for_ (runExcept (KE.eventToKeyboardEvent a)) \ke -> do
let co = KE.key ke
log "Key Pressed : "
for_ is actually a highly generic function that can be used for a variety of stuff, but this usage of it to absorb failures silently is pretty handy when writing code against the ugly DOM API.

What's wrong when use mypy to check type

from typing import Dict, List, Any, AnyStr, TypeVar
def abc(xyz: str) -> Dict[AnyStr, Any]:
return {"abc": 1}
And I use mypy to check this file. It's giving an Error.
Below is the Error message
"Dict entry 0 has incompatible type "str": "int"; expected "bytes":
"Any""
But I don't know why
The issue is that AnyStr is actually an alias for a typevar. This means your program is actually exactly equivalent to writing:
from typing import Dict, Any, AnyStr, TypeVar
T = TypeVar('T', str, bytes)
def abc(xyz: str) -> Dict[T, Any]:
return {"abc": 1}
This, however, presents us with a problem: how is mypy supposed to infer which of the two possible alternatives you wanted for T?
There are three possible fixes. You can either...
Find some way of using AnyStr at least two or more times within your type signature. For example, perhaps you decide this is really more what you meant?
def abc(xyz: AnyStr) -> Dict[AnyStr, Any]:
# etc
Use Union[str, bytes] instead of AnyStr:
from typing import Union, Dict, Any
def abc(xyz: str) -> Dict[Union[str, bytes], Any]:
# etc
If the type signature is starting to get uncomfortably long, you can shorten it by using a type alias:
from typing import Union, Dict, Any
# Note: this isn't a great type alias name, but whatever
UnionStr = Union[str, bytes]
def abc(xyz: str) -> Dict[UnionStr, Any]:
# etc

How do a build a lens for passing to purescript's `Thermite.focus`?

I am following various Thermite tutorials about setting up task lists. The only tutorial with a lot of explanation is also quite far out of date, so I am modifying it to fit the current Thermite. However, I have one call in which I cannot make the data types match.
import Optic.Lens (lens)
import Optic.Prism (prism)
import Optic.Types (Prism', Lens')
import Thermite as T
_TaskAction :: Prism' TaskListAction (Tuple Int TaskAction)
_TaskAction = ...
_tasks :: Lens' TaskListState (L.List TaskState)
_tasks = lens _.tasks (_ { tasks = _ })
taskList :: T.Spec _ TaskListState _ TaskListAction
taskList = T.focus _tasks _TaskAction taskSpec
However, this gives me an error message:
Could not match type
p0
with type
Function
while trying to match type p0 t1
with type Function
(List
{ text :: String
}
)
while checking that expression _tasks
has type p0 t1 t1 -> p0 t2 t2
in value declaration taskList
where p0 is a rigid type variable
bound at line 213, column 20 - line 213, column 26
t1 is an unknown type
t2 is an unknown type
The error message is specifically talking about the _tasks parameter I am passing to T.focus. But I do not know what the error is trying to tell me. I also know that the type signature for T.focus is...
focus :: forall eff props state2 state1 action1 action2.
Lens' state2 state1
-> Prism' action2 action1
-> Spec eff state1 props action1
-> Spec eff state2 props action2
So, the first parameter is a lens.
More frustratingly, I've checked more modern (but larger and less comprehensible) example code, and it shows exactly the same definition for _tasks as I have here.
So, what does this error message mean, and what do I need to do to fix it?
The fact that you're importing Optic.Lens suggests that you're using the wrong lens library here. purescript-lens provides traditional van-Laarhoven lenses (like Haskell's lens library), but Thermite uses the profunctor-lenses library.

liftEff Effect row type unification

I’m having a problem getting the following to compile:
module ContrivedExample where
import Prelude
import Data.Either (Either(..))
import Control.Monad.Eff (Eff)
import Control.Monad.Eff.Exception (EXCEPTION, throw)
import Control.Monad.Eff.Console (CONSOLE, log)
import Control.Monad.Eff.Class (liftEff)
import Control.Monad.Aff (launchAff)
import Control.Monad.Aff.Console (log) as A
contrivedExample :: forall e. Either String String -> Eff (exception :: EXCEPTION, console :: CONSOLE | e) Unit
contrivedExample a = do
_ <- launchAff do
_ <- A.log "yay"
liftEff $ case a of
Left e -> log e
Right a -> throw a
pure unit
I get this error:
Could not match type
( console :: CONSOLE
| e3
)
with type
( exception :: EXCEPTION
, console :: CONSOLE
| t2
)
If I remove exception from the Effect row, I get an error on the other side of the Either. Is there a better alternative to liftEff or some way I can unify the types?
As per the documentation of launchAff:
Converts the asynchronous computation into a synchronous one. All
values are ignored, and if the computation produces an error, it is
thrown.
Catching exceptions by using catchException with the resulting Eff
computation is not recommended, as exceptions may end up being thrown
asynchronously, in which case they cannot be caught.
If you do need to handle exceptions, you can use runAff instead, or
you can handle the exception within the Aff computation, using
catchError (or any of the other mechanisms).
I believe you cannot throw exceptions inside launchAff, unless you also catch them inside the same computation. You can only carry out other effects inside the launchAff computation otherwise.

Understanding Haskell postgresql connection function type error

I am java programmer reading and learning haskell now. I am trying write a simple program to connect (and disconnect) to postgres database using HDBC postgres driver. For simplicity i don't have any other logic in it.
It is throwing a function type error. I indented the code correct and if I remove disconnect then it works with the defined type.
Can some one shed some light on what i am missing defining the type for this function? i would apriciate your help.
thanks!
sample code:
import Database.HDBC
import Database.HDBC.PostgreSQL
import Database.HaskellDB
import Database.HaskellDB.HDBC.PostgreSQL
tryConnect :: Int -> (Database -> IO Connection) -> ()
tryConnect id =
do
c <- postgresqlConnect [("host","dbhost"),("dbname","db1"),("user","user1"),("password","test")]
disconnect c
return ()
I am getting the following error from GHCi
Couldn't match expected type `(Database -> IO Connection) -> a'
against inferred type `IO ()'
In a stmt of a 'do' expression: disconnect c
In the expression:
do { c <- postgresqlConnect
[("host", "dbhost"), ("dbname", "db1"), ....];
disconnect c;
return () }
In the definition of `insrt':
insrt id
= do { c <- postgresqlConnect [("host", "dbhost"), ....];
disconnect c;
return () }
Failed, modules loaded: none.
The problem is that you haven't provided enough arguments to postgresqlConnect. Its type signature is [(String, String)] -> (Database -> m a) -> m a, but you've only provided the first argument. Giving postgresqlConnect its second argument should solve the problem, and you'll be able to change the type declaration back to Int -> IO ().
EDIT: The answer below is totally wrong. My bad.
Well, the type signature is tryConnect :: Int -> (Database -> IO Connection) -> (). Generally this would indicate that the function takes an Int and a (Database -> IO Connection) and returns (), but the only parameter you've supplied in the function definition is id. Therefore, you actually have a function that takes an Int and returns a new function with the type signature (Database -> IO Connection) -> ().
This would be fine, except that the body of the function does not match this signature. The do expression returns an IO () value rather than the expected function, so you get an error because the compiler got a different return value than expected.
So, to conclude, it seems there's a parameter in the type signature that you haven't used in the actual function. Either remove that function from the type signature, or change the function to be tryConnect id func = ... rather than tryConnect id = ....