I want to use string literal as Traversal, but I am a bit lost in types.
Is it possible to create this instance?
import Control.Lens
import Data.Aeson
import Data.Aeson.Lens
import Data.String
import Data.Default
{- Having:
key' :: AsValue t => Text -> Traversal' t (Maybe Value)
_JSON :: (ToJSON a, FromJSON a) => Traversal' t a
-}
instance (AsValue t, FromJSON v, ToJSON v, Default v) => IsString (Traversal' t v) where
fromString k = key' (fromString k) . non (toJSON def) . _JSON
To achieve something like this inside State monad:
"some-key" .= (3 :: Int)
Problem with universally quantified type instances. Thanks!
I couldn't get your code to compile, but that shouldn't matter. I assume that you have a function of type
fromStringTraversal :: (AsValue t, FromJSON v, ToJSON v, Default v)
=> String -> Traversal' t v
fromStringTraversal = undefined
Then to write your instance, simply inline the definition of Traversal' into the instance head. This works because any type variables in an instance are universally quantified over implicitly anyways.
{-# LANGUAGE RankNTypes, FlexibleInstances, GADTs #-}
instance (a ~ a', b ~ b', AsValue b, Default a, FromJSON a, ToJSON a, Applicative f)
=> IsString ((a -> f a') -> b -> f b') where
fromString = fromStringTraversal
The a ~ a', b ~ b' constraints could be moved from the context to the instance head, but this way gives better type inference. Then
{-# LANGUAGE OverloadedStrings, NoMonomorphismRestriction #-}
-- Infered type:
-- test :: (AsValue s, MonadState s m) => m ()
test = "some-key" .= (3 :: Int)
Related
Motivation
I have a type, MyType, which is parametrised by a functor, f.
I want to use MyType Identity to represent "my view" of the data, and MyType Maybe to represent the type of updates to the data.
Problem
Is it possible to write an aeson ToJSON instance for MyType? I tried to use the ToJSON class, but I get an error (see bottom of post).
{-# LANGUAGE DeriveGeneric #-}
module Main where
import GHC.Generics
import Data.Aeson
data MyType f = MyType
{ age :: f Int
, name :: f String
} deriving(Generic)
instance ToJSON1 f => ToJSON (MyType f)
main :: IO ()
main = print . encode $ MyType (Just 1) (Just "hi")
How can I get a ToJSON instance for MyType f, for an arbitrary f?
Compilation error
Main.hs:12:10: error:
• Could not deduce (ToJSON (f String))
arising from a use of ‘aeson-1.2.4.0:Data.Aeson.Types.ToJSON.$dmtoJSON’
from the context: ToJSON1 f
bound by the instance declaration
at Main.hs:12:10-39
• In the expression:
aeson-1.2.4.0:Data.Aeson.Types.ToJSON.$dmtoJSON #MyType f
In an equation for ‘toJSON’:
toJSON = aeson-1.2.4.0:Data.Aeson.Types.ToJSON.$dmtoJSON #MyType f
In the instance declaration for ‘ToJSON (MyType f)’
|
12 | instance ToJSON1 f => ToJSON (MyType f)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Failed, no modules loaded.
Using my idea in the comment of exploiting the Lifting class, and after some tinkering I arrived at this
{-# LANGUAGE DeriveGeneric
, FlexibleContexts
, MultiParamTypeClasses
, ScopedTypeVariables
, TypeApplications
, UndecidableInstances
#-}
module Main where
import GHC.Generics
import Data.Aeson
import Data.Constraint
import Data.Constraint.Lifting
data MyType f = MyType
{ age :: f Int
, name :: f String
} deriving(Generic)
instance (Lifting ToJSON f) => ToJSON (MyType f) where
toJSON mt
| Sub Dict <- lifting #ToJSON #f #Int
, Sub Dict <- lifting #ToJSON #f #String
= genericToJSON defaultOptions mt
instance Lifting ToJSON Maybe where
lifting = Sub Dict
main :: IO ()
main = print . encode $ MyType (Just 1) (Just "hi")
Notes:
Dict converts back and forth between constraints (such as ToJSON Int) and values. Sub is just the constructor for constraint entailment.
lifting #ToJSON #f #Int is type application syntax.
I used genericToJSON defaultOptions by looking up the default implementation for toJSON. We just needed to manually bring some instances into scope with lifting first.
I hope this helps.
My aim is to write function that takes some polymorphic values and list with typereps representing concrete types. It returns new list with the same values but already casted to concrete types specified via typereps.
Let we have such list of values: ["one", "two"] with -XOverloadedStrings enabled.
Respectively, type of each one is IsString a => a.
List of typereps we could get in such way:
import Data.Typeable (Proxy(..), typeRep)
import Data.Text (Text)
[typeRep (Proxy :: Proxy String), typeRep (Proxy :: Proxy ByteString)]
Is there any way to get "one" of type String and "two" of type ByteString?
P.S. To prevent error according to list containing values of different types, we may wrap every value in Dynamic., as in the example below(pseudocode):
{-# LANGUAGE ParallelListComp #-}
import Data.Dynamic (toDyn)
[ toDyn (val :: type') | val <- vals | type' <- concreteTypes ]
It could be done using Template Haskell, but it will be too ugly.
I can't really imagine your purpose, but the code will probably look something like this. I'm using the new Type.Reflection interface because I'm more familiar with it than with the classic Data.Typeable, but that should work for this too.
import Type.Reflection
types :: [SomeTypeRep]
types = [SomeTypeRep (typeRep #String), SomeTypeRep (typeRep #Text)]
strings :: [String]
strings = ["one", "two"]
converted :: [Dynamic]
converted = fromJust $ zipWithM convert types strings
convert :: SomeTypeRep -> String -> Maybe Dynamic
convert (SomeTypeRep rep) s
| Just HRefl <- eqTypeRep rep (typeRep #String) = Just $ toDynamic s
| Just HRefl <- eqTypeRep rep (typeRep #Text) = Just $ toDynamic (fromString s)
| otherwise = Nothing
Hold my beer.
{-# LANGUAGE GADTs #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE OverloadedStrings #-}
import Data.ByteString (ByteString)
import Data.String
import Data.Text (Text)
data Forall c where Forall :: (forall a. c a => a) -> Forall c
data Exists c where Exists :: c a => a -> Exists c
data Evidence c where Evidence :: c a => proxy a -> Evidence c
instance c ~ IsString => IsString (Forall c) where
fromString s = Forall (fromString s)
asProxyType :: proxy a -> a -> a
asProxyType = const id
downcast :: Evidence c -> Forall c -> Exists c
downcast (Evidence proxy) (Forall v) = Exists (asProxyType proxy v)
polymorphicStrings :: c ~ IsString => [Forall c]
polymorphicStrings = ["one", "two"]
types :: c ~ IsString => [Evidence c]
types = [Evidence ([] :: [ByteString]), Evidence ([] :: [Text])]
monomorphicStrings :: c ~ IsString => [Exists c]
monomorphicStrings = zipWith downcast types polymorphicStrings
To connect with the question as asked: Exists Typeable is isomorphic to Dynamic. You might need to generalize Forall, Exists :: Constraint -> * to Forall, Exists :: [Constraint] -> * to comfortably support both IsString and Typeable at once, which is a bit of type-level hacking but nothing too strenuous. Type families can give you an Elem :: Constraint -> [Constraint] -> Bool which can be used to replace c ~ IsString everywhere above.
Given the code below which looks up type-specific information in Data.HashMap for a type, is it possible to define a new function getMapVal2 as documented in the comments, to build the TypeKey argument given the type?
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DataKinds #-}
import Data.Monoid ((<>))
import Data.Proxy (Proxy(Proxy))
import GHC.TypeLits (KnownSymbol, Symbol, symbolVal)
import qualified Data.HashMap.Strict as Map (HashMap, empty, insert, lookup)
import Data.Dynamic
import GHC.Generics
import Data.Maybe (fromJust, isNothing, maybe)
type family TypeKey (a :: *) :: Symbol where
TypeKey Int = "int"
TypeKey T = "trec"
data T = T { aInt :: Int} deriving (Show, Generic, Typeable)
extract ::(s ~ TypeKey a, Typeable a, KnownSymbol s) => Maybe Dynamic -> Maybe a
extract dyn = if (isNothing dyn) then Nothing else fromDynamic . fromJust $ dyn
getMapVal :: (s ~ TypeKey a, Typeable a, KnownSymbol s) => Map.HashMap String Dynamic -> String -> Maybe a
getMapVal m k = extract $ Map.lookup k m
{-- How do we get the TypeKey lookup for type a?
getMapVal2 :: (s ~ TypeKey a, Typeable a, KnownSymbol s) => Map.HashMap String Dynamic -> a -> Maybe a
getMapVal2 m ty = extract $ Map.lookup (symbolVal (Proxy :: Proxy (TypeKey ???))) m
--}
main = do
let map = Map.insert (symbolVal (Proxy :: Proxy (TypeKey T))) (toDyn $ T {aInt=5}) Map.empty -- we insert some value in hashmap for type T - it is of same type
val = getMapVal map (symbolVal (Proxy :: Proxy (TypeKey T))) :: Maybe T -- now let us retrieve the value in map for Type T. We pass the SymbolVal ourselves
--val = getMapVal2 map (T {aInt = 2}) -- now we want to lookup map value given something of a type T. Need getMapVal2 to build symbolval given the input type
print $ maybe "" show val -- prints value stored in Hashmap for type T which is: T {aInt=5}
This is just a toy code to test passing type specific configuration at run-time via Data.HashMap to a polymorphic function that acts on types of a typeclass.
Use the ScopedTypeVariables extension. This allows you to refer to forall-bound type variables in the body of the definition in which they are bound.
{-# LANGUAGE ScopedTypeVariables #-}
getMapVal2 :: forall a s. (s ~ TypeKey a, Typeable a, KnownSymbol s) => Map.HashMap String Dynamic -> a -> Maybe a
getMapVal2 m ty = extract $ Map.lookup (symbolVal (Proxy :: Proxy (TypeKey a))) m
TL,DR; Extending a constraint, ad-hoc...? My route is "forgetful", or un-equatable
Hello everyone, I'm currently trying my hand at making an overloaded function that could either take a constraint (in our case, IsString), or a data type with fields of the same constraint. Here is my code so far:
{-# LANGUAGE
OverloadedStrings
, FlexibleInstances
, UndecidableInstances
, InstanceSigs
, TypeFamilies
#-}
import Data.Monoid
class Bar a where
bar :: ( IsString b
, Monoid b ) => a -> b
-- | This instance won't work.
instance ( IsString a
, Monoid a ) => RelativeUrl a where
bar :: ( IsString b
, Monoid b
, a ~ b ) => a -> b
bar = id
-- | This is the data type "extending" #IsString#
data Foo a where
Foo :: ( IsString a, Monoid a ) =>
a -> Foo a
-- | This is where my dreams end :(
instance Bar (Foo a) where
bar :: ( IsString b
, Monoid b
, a ~ b ) => a -> b
bar (Foo a) = a
I realize that the instance signatures aren't kosher, and that's why (technically) this won't work, but is there any other way to do it? I'd ideally like all calls to bar be inferrable by the context - such that bar "foo" :: IsString a => a, without having to clamp-down the OverloadedString to an actual type.
Is there another way to achieve this? I'm open for crazy ideas :)
The Bar class is about being able to convert to anything that IsString. I presume the Monoid instance is there for some sort of efficiency. We could give Bar and bar more illuminating names.
class ToStringPlus a where
toStringPlus :: ( IsString b,
Monoid b ) => a -> b
You would like bar "foo" :: IsString a => a. With OverloadedStrings enabled "foo" :: IsString a -> a. You're asking how to convert a value that's already polymorphic over all instances of IsString to a value that's polymorphic over all instances of IsString. You don't need something like toStringPlus "foo" to do that, just use "foo".
Hiding IsString
If you'd like to turn the type forall a. IsString a => a into a data type you can do so with a GADT. It's not at all useful, since the only possible value of the type forall a. IsString a => a is fromString x where x :: String. This type can hold exactly the same values that String can hold, with none of the utility String provides.
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE RankNTypes #-}
import Data.String
data AString where
AString :: (forall a. IsString a => a) -> AString
instance IsString AString where
fromString x = AString (fromString x)
instance ToStringPlus AString where
toStringPlus (AString a) = a
Something more useful
AString wasn't very useful because it could only hold the same values as a String. The ToStringPlus class allows converting to something using more than just Strings, it also allows the Monoid operations of mappend, mconcat, and mempty. This means the type forall a. (IsString a, Monoid a) => a should be able to hold something different than just Strings.
data MonoidalString where
MonoidalString :: (forall a. (IsString a, Monoid a) => a) -> MonoidalString
MonoidalStrings form a Monoid. Notice that mconcat and mappend can't be written in points-free style due to the rank N types.
instance Monoid MonoidalString where
mempty = MonoidalString mempty
(MonoidalString x) `mappend` (MonoidalString y) = MonoidalString (x `mappend` y)
mconcat ms = MonoidalString (mconcat (map toStringPlus ms))
MonoidalStrings can also be instances of IsString and ToStringPlus in the same manner as AString from the previous section.
instance IsString MonoidalString where
fromString x = MonoidalString (fromString x)
instance ToStringPlus MonoidalString where
toStringPlus (MonoidalString a) = a
This lets us give meaning to your request in a comment "I'm trying to convert something that's already polymorphic over all instances of IsString and any Foo [to something that's polymorphic...]". We can combine using the Monoid operations something that's already polymorphic over all instances of IsString, "poly string", with a MonoidalString to get something that's polymorphic over all instances of IsString and Monoid.
Given something existing :: MonoidalString and "poly string" :: IsString a => a we can combine them with mappend.
existing :: MonoidalString
"poly string" :: IsString a => a
"poly string" `mappend` existing :: MonoidalString
toStringPlus ("poly string" `mappend` existing) :: (Monoid b, IsString b) => b
We can make a small example program using this to show off all the features of MonoidalString
main = do
let existing = ("MS" :: MonoidalString)
putStr . toStringPlus $ mconcat ["poly string", mempty `mappend` " ", existing]
Bar again
If you want to make a function bar that accepts arguments of both types forall a. Ctx a => a and D you can do so as long as there is an instance Ctx D. The type of the function is then D -> .... This works because a forall a. Ctx a => a can be used anywhere you need a D.
We can use this to write a bar for the last example.
bar :: (IsString a, Monoid a) => MonoidalString -> a
bar = toStringPlus
We can pass to bar a polymorphic string "foo" :: IsString a => a.
"foo" :: IsString a => a
bar "foo" :: (Monoid a, IsString a) => a
We can also pass to bar a monomorphic MonoidalString, existing :: MonoidalString
existing = ("MS" :: MonoidalString)
bar existing :: (Monoid a, IsString a) => a
I have some contrived type:
{-# LANGUAGE DeriveFunctor #-}
data T a = T a deriving (Functor)
... and that type is the instance of some contrived class:
class C t where
toInt :: t -> Int
instance C (T a) where
toInt _ = 0
How can I express in a function constraint that T a is an instance of some class for all a?
For example, consider the following function:
f t = toInt $ fmap Left t
Intuitively, I would expect the above function to work since toInt works on T a for all a, but I cannot express that in the type. This does not work:
f :: (Functor t, C (t a)) => t a -> Int
... because when we apply fmap the type has become Either a b. I can't fix this using:
f :: (Functor t, C (t (Either a b))) => t a -> Int
... because b does not represent a universally quantified variable. Nor can I say:
f :: (Functor t, C (t x)) => t a -> Int
... or use forall x to suggest that the constraint is valid for all x.
So my question is if there is a way to say that a constraint is polymorphic over some of its type variables.
Using the constraints package:
{-# LANGUAGE FlexibleContexts, ConstraintKinds, DeriveFunctor, TypeOperators #-}
import Data.Constraint
import Data.Constraint.Forall
data T a = T a deriving (Functor)
class C t where
toInt :: t -> Int
instance C (T a) where
toInt _ = 0
f :: ForallF C T => T a -> Int
f t = (toInt $ fmap Left t) \\ (instF :: ForallF C T :- C (T (Either a b)))