I am using Obelisk OAuth to handle multiple OAuth options (Facebook, Google, & Apple, though only FB is shown below).
I have getOAuthDetails which gets the correct details for each provider.
I use it in backend (at the bottom), but it gives me this error:
backend/src/Backend.hs:144:47-54: error:
• Couldn't match expected type ‘OAuthProvider’
with actual type ‘OAuth a1’
• In the first argument of ‘getOAuthDetails’, namely ‘provider’
In the expression: getOAuthDetails provider code
In a pattern binding: (t, reqUrl) = getOAuthDetails provider code
• Relevant bindings include
oauthRoute :: a1 (bound at backend/src/Backend.hs:140:41)
provider :: OAuth a1 (bound at backend/src/Backend.hs:140:29)
|
144 | let (t, reqUrl) = getOAuthDetails provider code
| ^^^^^^^^
This error is confusing to me because I am explicitly feeding in OAuthProvider as provider in that function. Why would it then say actual type ‘OAuth a1’?
data OAuthProvider = OAuthProvider_Facebook
| OAuthProvider_Google
| OAuthProvider_Apple
getOAuthDetails :: OAuthProvider -> T.Text -> (TokenRequest BackendRoute, String) -- NB. this last T.Text is a reqUrl
getOAuthDetails OAuthProvider_Facebook code = (fbTokenReq code, (T.unpack fbReqUrl))
getOAuthDetails OAuthProvider_Google code = (googTokenReq code, (T.unpack googReqUrl))
getOAuthDetails OAuthProvider_Apple code = (applTokenReq code, (T.unpack applReqUrl))
fbTokenReq :: T.Text -> TokenRequest BackendRoute
fbTokenReq code = TokenRequest
{ _tokenRequest_grant = TokenGrant_AuthorizationCode $ T.encodeUtf8 code
, _tokenRequest_clientId = "fake-client-id" -- Get this from the OAuth authorization server
, _tokenRequest_clientSecret = "fake-client-secret" -- Get this from the OAuth authorization server
, _tokenRequest_redirectUri = BackendRoute_OAuth
}
fbReqUrl :: T.Text
fbReqUrl = "https://graph.facebook.com/oauth/access_token"
...
-- provider2Auth :: OAuthProvider -> OAuth RedirectUriParams
-- provider2Auth
backend :: OAuthProvider -> Backend BackendRoute FrontendRoute
backend provider = Backend
{ _backend_run = \serve -> do
cfg <- getConfigs
let route = T.strip $ T.decodeUtf8 $ cfg ! "common/route"
tlsMgr <- Https.newTlsManager
serve $ \case
BackendRoute_Missing :/ () -> error "404"
BackendRoute_OAuth :/ provider :/ oauthRoute -> case oauthRoute of
OAuth_RedirectUri :/ redirectParams -> case redirectParams of
Nothing -> liftIO $ error "Expected to receive the authorization code here"
Just (RedirectUriParams code _mstate) -> do
let (t, reqUrl) = getOAuthDetails provider code
rsp <- liftIO $ flip Http.httpLbs tlsMgr =<< getOauthToken reqUrl route checkedEncoder t
-- ^ this response should include the access token and probably a refresh token
liftIO $ print rsp
, _backend_routeEncoder = fullRouteEncoder
}
EDIT:
ob run displayed this:
backend/src/Backend.hs:140:9-43: error:
• Could not deduce: a1 ~ R OAuth
from the context: a ~ Data.Dependent.Sum.DSum OAuth Identity
bound by a pattern with constructor:
BackendRoute_OAuth :: BackendRoute (R OAuth),
in a case alternative
at backend/src/Backend.hs:139:7-24
‘a1’ is a rigid type variable bound by
a pattern with pattern synonym:
:/ :: forall (f :: * -> *). () => forall a. f a -> a -> R f,
in a case alternative
at backend/src/Backend.hs:139:7-36
• In the pattern: OAuth_RedirectUri :/ redirectParams
In a case alternative:
OAuth_RedirectUri :/ redirectParams
-> case redirectParams of
Nothing
-> liftIO $ error "Expected to receive the authorization code here"
Just (RedirectUriParams code _mstate)
-> do let ...
....
In the expression:
case oauthRoute of {
OAuth_RedirectUri :/ redirectParams
-> case redirectParams of
Nothing
-> liftIO $ error "Expected to receive the authorization code here"
Just (RedirectUriParams code _mstate) -> do ... }
• Relevant bindings include
oauthRoute :: a1 (bound at backend/src/Backend.hs:139:41)
provider :: OAuth a1 (bound at backend/src/Backend.hs:139:29)
|
140 | OAuth_RedirectUri :/ redirectParams -> case redirectParams of
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
backend/src/Backend.hs:143:47-54: error:
• Couldn't match expected type ‘OAuthProvider’
with actual type ‘OAuth a1’
• In the first argument of ‘getOAuthDetails’, namely ‘provider’
In the expression: getOAuthDetails provider code
In a pattern binding: (t, reqUrl) = getOAuthDetails provider code
• Relevant bindings include
oauthRoute :: a1 (bound at backend/src/Backend.hs:139:41)
provider :: OAuth a1 (bound at backend/src/Backend.hs:139:29)
|
143 | let (t, reqUrl) = getOAuthDetails provider code
| ^^^^^^^^
Thanks to Srid, I saw that I had two providers. I modified my backend to:
backend :: OAuthProvider -> Backend BackendRoute FrontendRoute
backend provider = Backend
{ _backend_run = \serve -> do
cfg <- getConfigs
let route = T.strip $ T.decodeUtf8 $ cfg ! "common/route"
tlsMgr <- Https.newTlsManager
serve $ \case
BackendRoute_Missing :/ () -> error "404"
BackendRoute_OAuth :/ oauthRoute -> case oauthRoute of
OAuth_RedirectUri :/ redirectParams -> case redirectParams of
Nothing -> liftIO $ error "Expected to receive the authorization code here"
Just (RedirectUriParams code _mstate) -> do
let (t, reqUrl) = getOAuthDetails provider code
rsp <- liftIO $ flip Http.httpLbs tlsMgr =<< getOauthToken reqUrl route checkedEncoder t
-- ^ this response should include the access token and probably a refresh token
liftIO $ print rsp
, _backend_routeEncoder = fullRouteEncoder
}
And now I get:
Running test...
<interactive>:623:116-130: error:
• Couldn't match expected type ‘Backend
backendRoute0 FrontendRoute’
with actual type ‘OAuthProvider
-> Backend BackendRoute FrontendRoute’
• Probable cause: ‘backend’ is applied to too few arguments
In the third argument of ‘Obelisk.Run.run’, namely ‘backend’
In the expression:
Obelisk.Run.run
55245
(Obelisk.Run.runServeAsset
"/Users/levelchart/Documents/git/app-obelisk/static")
backend
Frontend.frontend
In an equation for ‘it’:
it
= Obelisk.Run.run
55245
(Obelisk.Run.runServeAsset
"/Users/levelchart/Documents/git/app-obelisk/static")
backend
Frontend.frontend
Related
Sorry for asking another question about this dice game :).
I've got the following code:
--type GepakteStenen = [Steen]
--data Tactiek = Tactiek
-- {tactiekPakken ::GepakteStenen -> Worp -> IO Steen
--, tactiekDoorgaan ::GepakteStenen -> IO Bool
-- }
tactiekUitv :: Worp -> GepakteStenen -> Predicaat -> IO(Steen,Bool)
tactiekUitv w g p = do s <- (tactiekPakken g w)
let gs = g ++ filter (s==) w
if (magStoppen p gs) then
return (s,tactiekDoorgaan gs)
else
return (s,True)
Where i get this error message:
Couldn't match expected type `Tactiek' with actual type `[Steen]'
Why is it that i am giving the right type to my "tactiekDoorgaan" function, it wants me to create that a Tactiek of that?
This declaration
data Tactiek = Tactiek
{ tactiekPakken :: GepakteStenen -> Worp -> IO Steen
...
makes tactiekPakken a three argument function
tactiekPakken :: Tactiek -> GepakteStenen -> Worp -> IO Steen
-- ^^^^^^^ --
because there is a first, implicit argument having your record type. this might be confusing at first, but after all, we can not access a record field without having a record value at hand.
In your code, you do not provide the implicit Tactiek argument
do s <- (tactiekPakken g w)
-- ^^^^^^^^^^^^^^ --
I am currently working on a game engine in Haskell and now I am trying to implement a messaging system between subsystems of this engine (I am using a Publisher-Subscriber pattern). You can check out the code as a whole at here on my github profile. I implemented a "Participant" typeclass for all datatypes participating in my Messaging system as follows:
class (Show m, Message m) => Participant prt m where
-- | Function to get the lsit of subscribers from the participant
partSubscribers
:: prt
-- ^ the participant
-> forall us. Affection us [(m -> Affection us ())]
-- ^ List of Subscriber functions
-- | Subscribe to the 'Participant''s events
partSubscribe
:: prt
-- ^ The 'Participant' to subscribe to
-> (forall us. m -> Affection us ())
-- ^ What to do in case of a 'Message'
-- (Subscriber function)
-> Affection us UUID
-- ^ 'UUID' of the registered subscriber Function
-- | Unsubscribe a Subscriber function from Participant
partUnSubscribe
:: prt -- ^ The 'Participant' to unsubscribe from
-> UUID -- ^ The subscriber function's 'UUID'
-> Affection us ()
-- | Get the 'Participant' to emit a 'Message' on all of its subscribers
partEmit
:: prt -- ^ The 'Participant'
-> m -- ^ The 'Message' to emit
-> Affection us ()
partEmit p m = do
liftIO $ logIO Debug $ "Emitting message: " ++ show m
l <- partSubscribers p
mapM_ ($ m) l
A concrete implementation of this Typeclass looks like this. I'm making the use of the Package stm:
data AffectionWindow us = AffectionWindow
{ windowSubscribers
:: forall us. TVar [(UUID, WindowMessage -> Affection us ())]
}
instance Participant (AffectionWindow us) WindowMessage where
partSubscribe p funct = do
uuid <- genUUID
liftIO $ atomically $ modifyTVar' (windowSubscribers p) ((uuid, funct) :)
return uuid
partUnSubscribe p uuid =
liftIO $ atomically $ modifyTVar' (windowSubscribers p)
(filter (\(u, _) -> u /= uuid))
partSubscribers p = do
subTups <- liftIO $ readTVarIO $ windowSubscribers p
return $ map snd subTups
This code compiles fine as a library, but when I try to use this in a minimal example, it fails to compile. The code, where it fails looks like this:
load :: IO StateData
load = do
empty1 <- newTVarIO []
-- ([] :: [(UUID, WindowMessage -> Affection StateData ())])
empty2 <- newTVarIO []
-- ([] :: [(UUID, MouseMessage -> Affection StateData ())])
empty3 <- newTVarIO []
-- ([] :: [(UUID, KeyboardMessage -> Affection StateData ())])
return $ StateData $ Subsystems
(AffectionWindow empty1)
(AffectionMouse empty2)
(AffectionKeyboard empty3)
and the error message is:
examples/example00.hs:43:22: error:
• Couldn't match type ‘a0’
with ‘(UUID, WindowMessage -> Affection us1 ())’
because type variable ‘us1’ would escape its scope
This (rigid, skolem) type variable is bound by
a type expected by the context:
forall us1. TVar [(UUID, WindowMessage -> Affection us1 ())]
at examples/example00.hs:43:6-27
Expected type: TVar [(UUID, WindowMessage -> Affection us1 ())]
Actual type: TVar [a0]
• In the first argument of ‘AffectionWindow’, namely ‘empty1’
In the first argument of ‘Subsystems’, namely
‘(AffectionWindow empty1)’
In the second argument of ‘($)’, namely
‘Subsystems
(AffectionWindow empty1)
(AffectionMouse empty2)
(AffectionKeyboard empty3)’
• Relevant bindings include
empty1 :: TVar [a0] (bound at examples/example00.hs:39:3)
|
43 | (AffectionWindow empty1)
| ^^^^^^
I have never encountered any error like this and I'm at my wits end.
Hopefully somebody in here knows the solution.
Thanks for your time!
How do I set per request transient-state in Snap?
Looking at http://snapframework.com/docs/tutorials/snaplets-tutorial it says
Handler b v has a MonadState v instance. This means that you can access all your snaplet state through the get, put, gets, and modify functions that are probably familiar from the state monad.
So I am trying the following:
data App = App
{ _test :: Int
}
And then in the handler:
someHandler :: Handler App App ()
someHandler = do
test <- gets _test
liftIO $ print test
put ( 2 :: Int _test
However I get the error:
• Couldn't match type ‘()’ with ‘Handler App App a0’
Expected type: (App -> Int) -> Handler App App a0
Actual type: (App -> Int) -> ()
• The function ‘put’ is applied to two arguments,
its type is ‘s0 -> m0 ()’,
it is specialized to ‘Int -> (App -> Int) -> ()’
In a stmt of a 'do' block: put (2 :: Int) _test
In the expression:
do { test <- gets _test;
liftIO $ print test;
put (2 :: Int) _test;
Any ideas how to achieve that?
Thanks!
I'm really struggling to square this circle.
getPostContent uses Wreq to download a blog post and return it.
getPostContent url = do
let opts = defaults & W.checkStatus .~ (Just $ \_ _ _ -> Nothing)
postResp <- getWith opts $ baseUrl ++ url
if postResp ^. W.responseStatus . statusCode == 200
-- then return $ LEnc.encodeUtf8 $ postResp ^. W.responseBody . _String -- :: Prism T Text
then return $ postResp ^. W.responseBody . _String
else return "error downloading"
This is consumed by parseLBS
do
page <- getPostContent r -- :: IO String
let
-- parseLBS :: Data.ByteString.Lazy.Internal.ByteString -> Text.XML.Document
cursor = fromDocument $ parseLBS page
As I understand it, getPostContent is providing Data.Text.Text, whereas I need Data.ByteString.Lazy.Internal.ByteString and I cannot work out how to convert them ( thought it should be this, see code snippet above, but it does not compile either).
Couldn't match expected type ‘Data.ByteString.Lazy.Internal.ByteString’
with actual type ‘T.Text’
In the first argument of ‘parseLBS’, namely ‘page’
In the second argument of ‘($)’, namely ‘parseLBS page’
Compilation message with encode uncommented
Couldn't match type ‘TL.Text’
with ‘T.Text’
NB: ‘TL.Text’ is defined in ‘Data.Text.Internal.Lazy’
‘T.Text’ is defined in ‘Data.Text.Internal’
Expected type: (TL.Text -> Const TL.Text TL.Text)
-> Data.ByteString.Lazy.Internal.ByteString
-> Const TL.Text Data.ByteString.Lazy.Internal.ByteString
Actual type: (T.Text -> Const TL.Text T.Text)
-> Data.ByteString.Lazy.Internal.ByteString
-> Const TL.Text Data.ByteString.Lazy.Internal.ByteString
In the second argument of ‘(.)’, namely ‘_String’
In the second argument of ‘(^.)’, namely ‘responseBody . _String’
To summarise: encodeUtf8 is the right way to go. It seems the one you're using is from Data.Text.Lazy.Encoding, which requires a lazy Text. You can use Data.Text.Lazy.fromStrict to convert... Or you can look into Data.Text.Encoding, which works on strict Text (but then gives you a strict ByteString...)
I'm just starting Haskell web development using Spock, persistent and blaze-html.
In one of the routes I have, I want to load every row in my selected tables. I do something like this:
get ("/show/flight/" <//> (var :: Var Integer)) $ \f -> requireUser $ \(_, l) -> do
fs <- runSQL $ loadFlightInfos f
case fs of
[] -> blaze $ template False (showResultAlertBar False "Oops, something went wrong! Please try again.")
_ -> blaze $ template True (H.toHtml $ usersUsername l) loadFlightSeat
where
loadFlightSeat :: H.Html
loadFlightSeat =
forM_ fs $ \fs' -> do
sid <- runSQL $ getSeatIdByFlight fs' c
case sid of
Nothing -> H.div H.! A.class_ "alert alert-danger" $ "Oops, something went wrong! Please try again."
Just rid -> H.a H.! A.href (H.toValue $ "/flight/seat/" <> show c <> "/" <> show (fromIntegral $ (fromSqlKey . entityKey) sid)) H.! A.class_ "btn btn-theme" $ H.toHtml fs'
loadFlightInfos has type:
Integer -> SqlPersistM [Entity Flight]
and getSeatIdByFlight:
T.Text -> Integer -> SqlPersistM (Maybe (Entity Flight))
I copied runSQL from Spock's blog sample app, and it's something like this:
runSQL :: (HasSpock m, SpockConn m ~ SqlBackend) => SqlPersistT (NoLoggingT (ResourceT IO)) a -> m a
runSQL action = runQuery $ \conn -> runResourceT $ runNoLoggingT $ runSqlConn action conn
The type error I got:
Couldn't match expected type ‘SqlBackend’
with actual type ‘SpockConn Text.Blaze.Internal.MarkupM’
In the expression: runSQL
In a stmt of a 'do' block:
sid <- runSQL $ getSeatIdByFlight fs' c
I still don't understand this type error, because I know runSQL is a wrapper from persistent to Spock and if I simply just want to output HTML, why can't it pass type checking?
How do I resolve this type error?
Disclaimer: I didn't run your code.
I know runSQL is a wrapper from persistent to Spock
Exactly, and there lies your issue. The type of runSQL is:
runSQL :: (HasSpock m, SpockConn m ~ SqlBackend)
=> SqlPersistT (NoLoggingT (ResourceT IO)) a -> m a
The result type, (HasSpock m, SpockConn m ~ SqlBackend) => m a, tells us that runSQL gives a result in the Spock monad. Therefore, loadFlightSeat should be a Spock monad computation as well. However, you gave it type H.Html, which has nothing to do with the Spock monad. The issue will probably go away if you remove the mistaken type signature of loadFlightSeat and adjust your usage of loadFlightSeat accordingly:
flightSeat <- loadFlightSeat -- Returns an H.Html value in the Spock monad.
case fs of
[] -> blaze $ template False (showResultAlertBar False "Oops, something went wrong! Please try again.")
_ -> blaze $ template True (H.toHtml $ usersUsername l) flightSeat
P.S.: The type error you got...
Couldn't match expected type ‘SqlBackend’
with actual type ‘SpockConn Text.Blaze.Internal.MarkupM’
In the expression: runSQL
In a stmt of a 'do' block:
sid <- runSQL $ getSeatIdByFlight fs' c
...is unusually weird because H.Html happens to be a synonym for MarkupM (), with MarkupM being a monad defined by blaze. As a consequence, the signature you gave to loadFlightSeat leads the compiler to attempt matching Spock's monad with MarkupM.