I'm trying to use ImplicitParams
together with Rank2Types
in order to build a custom Monad for my application. However, I get the error below, which I don't understand.
Here is a complete example:
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE ImplicitParams #-}
{-# LANGUAGE Rank2Types #-}
{-# LANGUAGE MultiParamTypeClasses #-}
import Control.Monad.Reader
import Control.Monad.Random hiding (random)
import Control.Monad.Random.Class
data Hint = Hint { good :: Int
, bad :: Int }
deriving (Eq, Show)
class Combination c where
random :: Env c
fromList :: [Int] -> Env c
toList :: c -> Env [Int]
class Combination c => Set s c where
empty :: (?combination :: c) => s
fromList' :: [c] -> s
member :: c -> s -> Bool
insert :: c -> s -> s
notMember :: c -> s -> Bool
notMember c s = not (member c s)
type Env' a = Rand StdGen a
newtype Env a = Env { unenv :: forall i1 i2 c s .
Integral i1
=> Integral i2
=> Combination c
=> Set s c
=> (?colors :: Int)
=> (?holes :: Int)
=> (?hint :: c -> c -> Env Hint)
=> (?combination :: c)
=> (?set :: s)
=> (?cardinality :: i1)
=> (?powers :: [i2])
=> Env' a }
instance Functor Env where
fmap f (Env a) = Env $ fmap f a
instance Applicative Env where
pure = Env . pure
(Env f) <*> (Env a) = Env $ f <*> a
instance Monad Env where
(Env m) >>= f = Env $ m >>= \a -> let (Env b) = f a in b
instance MonadRandom Env where
getRandomR = Env . getRandomR
getRandom = Env getRandom
getRandomRs = Env . getRandomRs
getRandoms = Env getRandoms
-- Dummy instances
instance Combination Int where
random = undefined
fromList _ = undefined
toList _ = undefined
instance Set Float Int where
empty = undefined
fromList' = undefined
member = undefined
insert = undefined
-- Tests
f :: forall c i.
Combination c
=> Integral i
=> (?colors :: Int)
=> (?cardinality :: i)
=> (?combination :: c)
=> (?hint :: c -> c -> Env Hint)
=> c -> Env String
f _ = do
r <- getRandomR (100::Int, 200)
secret <- random :: Env c
r1 <- random :: Env c
h <- (?hint secret r1)
return $
show ?colors
++ " " ++ (show $ fromIntegral ?cardinality)
++ " " ++ show r
g :: forall c.
Combination c
=> (?combination :: c)
=> Env String
g = do
r <- random :: Env c
Env $ unenv $ f r
-- ^^^
-- ERROR: Couldn't match type ‘c1’ with ‘c’...
I tried almost everything to make it work without any success, here is the error message :
Internal.hs:92:17: error:
• Couldn't match type ‘c1’ with ‘c’
arising from a functional dependency between constraints:
‘?combination::c’ arising from a use of ‘f’ at Internal.hs:92:17-19
‘?combination::c1’
arising from a type expected by the context:
forall i1 i2 c s.
(Integral i1, Integral i2, Combination c, Set s c, ?colors::Int,
?holes::Int, ?hint::c -> c -> Env Hint, ?combination::c, ?set::s,
?cardinality::i1, ?powers::[i2]) =>
Env' a0
at Internal.hs:92:3-19
‘c1’ is a rigid type variable bound by
a type expected by the context:
forall i1 i2 c1 s.
(Integral i1, Integral i2, Combination c1, Set s c1, ?colors::Int,
?holes::Int, ?hint::c1 -> c1 -> Env Hint, ?combination::c1,
?set::s, ?cardinality::i1, ?powers::[i2]) =>
Env' String
at Internal.hs:92:3-19
‘c’ is a rigid type variable bound by
the type signature for:
g :: forall c. (Combination c, ?combination::c) => Env String
at Internal.hs:(86,1)-(89,15)
• In the second argument of ‘($)’, namely ‘f r’
In the second argument of ‘($)’, namely ‘unenv $ f r’
In a stmt of a 'do' block: Env $ unenv $ f r
• Relevant bindings include r :: c (bound at Internal.hs:91:3)
|
92 | Env $ unenv $ f r
| ^^^
The point is that I don't want to add every implicit parameter to the signature of every single function in my program in case I want to add other parameters in the future.
Thanks.
EDIT: I just discovered PartialTypeSignatures
which could solve my problem in a much simpler way, I'm trying this out now!
Instead of
Env $ unenv $ f r
you should simply use
f r
Doing Env $ unenv $ ...
introduces a new bunch of rigid type variables i1 i2 cs
, which have to be matched by unenv $ ...
and GHC meets some ambiguity here, I guess. There should be some way to force unenv $ ...
to use the exact same rigid variables, but it's not worth it, since we can avoid that.
Further, to call fr
you also have to define the other implicit arguments like ?cardinality, ?colors, ?hint
. Otherwise, you'll still get an error.
Ok so I came up with a solution using PartialTypeSignatures
. I could have avoided signatures altogether, but in some cases I need to bring in some constraints. The only downside is that I still need to define full signatures on type class methods, but I can live with it. Here is an example :
{-# LANGUAGE PartialTypeSignatures #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE ImplicitParams #-}
class SomeClass a where
method :: Real t => (?var1 :: t) => IO a
instance SomeClass Int where
method = return $ floor $ realToFrac $ ?var1 + 1
instance SomeClass Float where
method = return $ realToFrac $ ?var1 + 1
someFun :: _ => SomeClass a => a -> IO a
someFun x = do
v <- method
return $ v + ?var2
f :: forall t. _ => SomeClass t => (?proxy :: t) => IO t
f = (method :: IO t) >>= ?fun
g :: _ => Num t => IO t
g = do
v <- f
return $ v + 1
v1 :: IO Int
v1 =
let
?var1 = 10
?var2 = 20
?proxy = undefined :: Int
in let
?fun = someFun
in g
v2 :: IO Float
v2 =
let
?var1 = 10.1
?var2 = 20.2
?proxy = undefined :: Float
in let
?fun = someFun
in g
main :: IO ()
main = do
r1 <- v1
r2 <- v2
print r1
print r2
I'm still open to suggestions though :) .
Side note: One downside of implicit parameters is that you must bring them into scope as soon as you want to reference a function or a value with implicit parameters constraints. As a consequence you cannot compose those functions without bringing all their parameters into scope. A potential solution to this problem is explained in this blog post .
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.