Making a typeclass, cannot deduce from context
I'm using the Servant libary, and I would like to automatically map results into error codes. Servant expects the type: Either (Int, String) a
.
For example, if I have a model function of type: IO (Maybe User)
. I would like to turn that in to (404, "Not Found")
on Nothing, and the User
if it's there.
To do this, I'm writing a typeclass!
class Servile a where
toStatus :: ToJSON val => a -> Either (Int, String) val
instance ToJSON a => Servile (Maybe a) where
toStatus Nothing = Left (404, "Not Found")
toStatus (Just a) = Right a
I have other instances I'd like to write, but this one gives me the error:
Could not deduce (a ~ val)
from the context (ToJSON a)
bound by the instance declaration at Serials/Api.hs:90:10-38
or from (ToJSON val)
bound by the type signature for
toStatus :: ToJSON val => Maybe a -> Either (Int, String) val
at Serials/Api.hs:91:5-12
‘a’ is a rigid type variable bound by
the instance declaration at Serials/Api.hs:90:10
‘val’ is a rigid type variable bound by
the type signature for
toStatus :: ToJSON val => Maybe a -> Either (Int, String) val
at Serials/Api.hs:91:5
Relevant bindings include
a :: a (bound at Serials/Api.hs:92:20)
toStatus :: Maybe a -> Either (Int, String) val
(bound at Serials/Api.hs:91:5)
In the first argument of ‘Right’, namely ‘a’
In the expression: Right a
What's the right way to do this?
另一种解决方案是,如果你想使用一些非参数化类型,则使用TypeFamilies
:
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE FlexibleInstances #-}
module Temp where
import Data.Monoid
class ToStatus a where
type Val a
toStatus :: a -> Either (Int, String) (Val a)
instance ToStatus (Maybe a) where
type Val (Maybe a) = a
toStatus Nothing = Left (404, "Not Found")
toStatus (Just v) = Right v
instance Show a => ToStatus (Either a b) where
type Val (Either a b) = b
toStatus (Left e) = Left (500, "Server Error: " <> show e)
toStatus (Right v) = Right v
instance ToStatus String where
type Val String = ()
toStatus "200 Success" = Right ()
toStatus err = Left (500, err)
I hopped on #haskell since I wasn't expressing the question very well. The issue is that it can't vary over any a
to produce a val
like that. It also turns out that ToJSON
had nothing to do with this problem.
This works: Note that I changed toStatus
to be a val
instead of a
, and the instances to remove the type variable.
class ToStatus a where
toStatus :: a val -> Either (Int, String) val
instance ToStatus Maybe where
toStatus Nothing = Left (404, "Not Found")
toStatus (Just v) = Right v
instance Show a => ToStatus (Either a) where
toStatus (Left a) = Left (500, "Server Error: " <> show a)
toStatus (Right v) = Right v
链接地址: http://www.djcxy.com/p/43534.html
上一篇: 根据返回类型选择一个类型类
下一篇: 制作一个类型类,不能从上下文中推导出来