Haskell: refer to type of value at compile time - haskell

I'm wondering if there's a nice way to refer to types of values without explicitly aliasing them using type in code (not at runtime - there is no reification going on here).
Take the following code (using Data.Vinyl):
{-# LANGUAGE DataKinds, TypeOperators #-}
import Data.Vinyl
name = Field :: "name" ::: String
age = Field :: "age" ::: Int
type Person = ["name" ::: String, "age" ::: Int]
Here we have the types "name" ::: String and "age" ::: Int repeated in two places. If we reuse fields in multiple records, this can become multiple places. Despite the fact that the Person type is really referring to the constituent fields, the type declarations are independent. So changing age to be represented by Float, say, requires changes in various places.
Obviously it's not necessary to explicitly type things, since they will be inferred. However, in my case the record types are being returned from an options parser, and thus exported. Likewise, one could write the following:
type Name = "name" ::: String
name = Field :: Name
type Age = "age" ::: Int
age = Field :: Age
type Person = [Name, Age]
However, this then involves another load of type aliases and double the number of lines. What I would like to be able to write is the following:
name = Field :: "name" ::: String
age = Field :: "age" ::: Int
type Person = [typeof name, typeof age]
This explicitly links the type of a Person to the types of its fields.
Is there a way (preferably sans-TH, but I'd be interested even involving TH) to do this?

It should be easy enough to make a String -> [Name] -> DecsQ function out of
the following. Too bad with ghc7.6 (at least), the check for cycles in type
synonyms seems to stop the prettier type Person = $(listOfT ['name, 'age]) from
working out.
{-# LANGUAGE DataKinds, TemplateHaskell, TypeOperators #-}
import Language.Haskell.TH
import Control.Applicative
import Data.Vinyl
name = Field :: "name" ::: String
age = Field :: "age" ::: Int
let listOfT (n:ns) = do
VarI _ ty _ _ <- reify n
(appT promotedConsT) (return ty) `appT` listOfT ns
listOfT [] = promotedNilT
in return <$> tySynD (mkName "Person") [] (listOfT ['name, 'age])

Related

How do I model a record's fields as data?

Let's say I have a Person record with some fields:
data Person = Person
{ name :: String
, age :: Int
, id :: Int
}
and I want to be able to search a list of Persons by a given field:
findByName :: String -> [Person] -> Maybe Person
findByName s = find (\p -> name p == s)
Now let's say I want to be able to model and store these searches/queries as data, for instance for logging purposes, or to batch execute them, or whatever.
How would I go about representing a search over a given field (or set of fields) as data?
My intuition says to model it as a map of fields to string values (Map (RecordField) (Maybe String)), but I can't do that, because record fields are functions.
Is there a better way to do this than, say, the following?
data PersonField = Name | Age | Int
type Search = Map PersonField (Maybe String)
This could technically work but it decouples PersonField from Person in an ugly way.
I want to be able to model and store these searches/queries as data
Let's assume we want to store them as JSON. We could define a type like
data Predicate record = Predicate {
runPredicate :: record -> Bool ,
storePredicate :: Value
}
Where storePredicate would return a JSON representation of the "reference value" inside the predicate. For example, the value 77 for "age equals 77".
For each record, we would like to have a collection like this:
type FieldName = String
type FieldPredicates record = [(FieldName, Value -> Maybe (Predicate record))]
That is: for each field, we can supply a JSON value encoding the "reference value" of the predicate and, if it parses successfully, we get a Predicate. Otherwise we get Nothing. This would allows us to serialize and deserialize predicates.
We could define FieldPredicates manually for each record, but is there a more automated way? We could try generating field equality predicates using a typeclass. But first, the extensions and imports dance:
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneKindSignatures #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE BlockArguments #-}
import Data.Functor ( (<&>) )
import Data.Kind ( Type, Constraint )
import Data.Proxy
import GHC.Records ( HasField(..) )
import GHC.TypeLits ( KnownSymbol, Symbol, symbolVal )
import Data.Aeson ( FromJSON(parseJSON), Value, ToJSON(toJSON) )
import Data.Aeson.Types (parseMaybe)
import Data.List ( lookup )
Now we define the helper typeclass:
type HasEqFieldPredicates :: [Symbol] -> Type -> Constraint
class HasEqFieldPredicates fieldNames record where
eqFieldPredicates :: FieldPredicates record
instance HasEqFieldPredicates '[] record where
eqFieldPredicates = []
instance
( KnownSymbol fieldName,
HasField fieldName record v,
Eq v,
FromJSON v,
ToJSON v,
HasEqFieldPredicates fieldNames record
) =>
HasEqFieldPredicates (fieldName ': fieldNames) record
where
eqFieldPredicates =
let current =
( symbolVal (Proxy #fieldName),
\j ->
parseMaybe (parseJSON #v) j <&> \v ->
Predicate (\record -> getField #fieldName record == v) (toJSON v))
in current : eqFieldPredicates #fieldNames #record
An example with Person:
personEqPredicates :: [(FieldName, Value -> Maybe (Predicate Person))]
personEqPredicates = eqFieldPredicates #["name", "age", "id"] #Person
personAgeEquals :: Value -> Maybe (Predicate Person)
personAgeEquals = let Just x = Data.List.lookup "age" personEqPredicates in x
Putting it to work:
ghci> let Just p = personAgeEquals (toJSON (77::Int)) in runPredicate p Person { name = "John", age = 78, id = 3 }
False
ghci> let Just p = personAgeEquals (toJSON (78::Int)) in runPredicate p Person { name = "John", age = 78, id = 3 }
True
If you don't need to serialize these query objects to disk, then your "field" type is Person -> a. A record accessor is just a function from Person to some type a. Or if you end up outgrowing basic accessors and need to work with a lot of nested data, you can look into lenses.
However, it sounds like you want to be able to write these queries to disk. In that case, you can't easily serialize functions (or lenses, for that matter). I don't know of a way built-in to Haskell to do all of that automatically and still have it be serializable. So my recommendation would be to roll your own datatypes.
data PersonField = Name | Age | Id
or, even better, you can use GADTs to keep type safety.
data PersonField a where
Name :: PersonField String
Age :: PersonField Int
Id :: PersonField Int
getField :: PersonField a -> Person -> a
getField Name = name
getField Age = age
getField Id = id
Then you have total control over this concrete type and can write your own serialization logic for it. I think Map PersonField (Maybe String) is a good start, and you can refine the Maybe String part if you end up doing more complex queries (like "contains" or "case insensitive comparison", for instance).

Silly duplicated record fields error

Consider the following:
{-# LANGUAGE DuplicateRecordFields #-}
data A = A { name :: String }
data B = B { name :: String }
main = print $ name (A "Alice")
When compiled, I get the following message (on GHC 8.0.2)
duplicatedrecords.hs:7:16: error:
Ambiguous occurrence ‘name’
It could refer to either the field ‘name’,
defined at duplicatedrecords.hs:5:14
or the field ‘name’, defined at duplicatedrecords.hs:3:14
But if I modify the main line as follows:
main = print $ name ((A "Alice") :: A)
Compilation proceeds successfully.
Why is this? The type signature :: A seems redundant to me, as surely the A constructor makes it clear to the compiler that (A "Alice") is of type A. But for some reason it makes a difference. Why is this and is there a way I can get this to compile without littering extra type signatures everywhere?
Note:
It's worth noting that the following compiles fine:
data A = A { a_name :: String }
data B = B { b_name :: String }
class Name t where
name :: t -> String
instance Name A where name = a_name
instance Name B where name = b_name
main = print $ name (A "Alice")
We can even go further as follows, allowing different result types:
{-# LANGUAGE TypeFamilies #-}
data A = A { a_name :: String }
data B = B { b_name :: Int }
class Name t where
type family T t
name :: t -> T t
instance Name A where
type T A = String
name = a_name
instance Name B where
type T B = Int
name = b_name
main = print $ name (A "Alice")
It seems like GHC just has to mechanically add a class for each unique record name and an instance for each record in each data type. This will mean however that name x == name y not implying that the types of x and y are the same but I'd expect that when using this extension anyway.
Just wondering if there's anything tricky I'm missing here regarding the implementation or that it just needs someone to do it?
-XDuplicateRecordFields currently doesn't infer types from arguments.
See GHC user guide section about this extension.
However, we do not infer the type of the argument to determine the datatype, or have any way of deferring the choice to the constraint solver. Thus the following is ambiguous:
But things are improving. So we might expect and finally get desired behavior:
https://prime.haskell.org/wiki/TypeDirectedNameResolution

Safe Record field query

Is there a clean way to avoid the following boilerplate:
Given a Record data type definition....
data Value = A{ name::String } | B{ name::String } | C{}
write a function that safely returns name
getName :: Value -> Maybe String
getName A{ name=x } = Just x
getName B{ name=x } = Just x
getName C{} = Nothing
I know you can do this with Template Haskell, I am looking for a cleaner soln than that, perhaps a GHC extension or something else I've overlooked.
lens's Template Haskell helpers do the right thing when they encounter partial record fields.
{-# LANGUAGE TemplateHaskell #-}
import Control.Applicative
import Control.Lens
data T = A { _name :: String }
| B { _name :: String }
| C
makeLenses ''T
This'll generate a Traversal' called name that selects the String inside the A and B constructors and does nothing in the C case.
ghci> :i name
name :: Traversal' T String -- Defined at test.hs:11:1
So we can use the ^? operator (which is a flipped synonym for preview) from Control.Lens.Fold to pull out Maybe the name.
getName :: T -> Maybe String
getName = (^? name)
You can also make Prism's for the constructors of your datatype, and choose the first one of those which matches using <|>. This version is useful when the fields of your constructors have different names, but you do have to remember to update your extractor function when you add constructors.
makePrisms ''T
getName' :: T -> Maybe String
getName' t = t^?_A <|> t^?_B
lens is pretty useful!
Why don't you use a GADT? I do not know if you are interested in using only records. But, I fell that GADTs provide a clean solution to your problem, since you can restrict what constructors are valid by refining types.
{-# LANGUAGE GADTs #-}
module Teste where
data Value a where
A :: String -> Value String
B :: String -> Value String
C :: Value ()
name :: Value String -> String
name (A s) = s
name (B s) = s
Notice that both A and B produce Value String values while C produces Value (). When you define function
name :: Value String -> String
it specifically says that you can only pass a value that has a string in it. So, you can only pattern match on A or B values. This is useful to avoid the need of Maybe in code.

Haskell get types of Data Constructor

I was wondering if given a constructor, such as:
data UserType = User
{ username :: String
, password :: String
} -- deriving whatever necessary
What the easiest way is for me to get something on the lines of [("username", String), ("password", String)], short of just manually writing it. Now for this specific example it is fine to just write it but for a complex database model with lots of different fields it would be pretty annoying.
So far I have looked through Typeable and Data but so far the closest thing I have found is:
user = User "admin" "pass"
constrFields (toConstr user)
But that doesn't tell me the types, it just returns ["username", "password"] and it also requires that I create an instance of User.
I just knocked out a function using Data.Typeable that lets you turn a constructor into a list of the TypeReps of its arguments. In conjunction with the constrFields you found you can zip them together to get your desired result:
{-# LANGUAGE DeriveDataTypeable #-}
module Foo where
import Data.Typeable
import Data.Typeable.Internal(funTc)
getConsArguments c = go (typeOf c)
where go x = let (con, rest) = splitTyConApp x
in if con == funTc
then case rest of (c:cs:[]) -> c : go cs
_ -> error "arrows always take two arguments"
else []
given data Foo = Foo {a :: String, b :: Int} deriving Typeable, we get
*> getConsArguments Foo
[[Char],Int]
As one would hope.
On how to get the field names without using a populated data type value itself, here is a solution:
constrFields . head . dataTypeConstrs $ dataTypeOf (undefined :: Foo)

How can I read the metadata of a type at runtime?

I'd like to write a program that prints out some metadata of a Haskell type. Although I know this isn't valid code, the idea is something like:
data Person = Person { name :: String, age :: Int }
metadata :: Type -> String
metadata t = ???
metadata Person -- returns "Person (name,age)"
The important restriction being I don't have an instance of Person, just the type.
I've started looking into Generics & Typeable/Data, but without an instance I'm not sure they'll do what I need. Can anyone point me in the right direction?
Reflection in Haskell works using the Typeable class, which is defined in Data.Typeable and includes the typeOf* method to get a run-time representation of a value's type.
ghci> :m +Data.Typeable
ghci> :t typeOf 'a'
typeOf 'a' :: TypeRep
ghci> typeOf 'a' -- We could use any value of type Char and get the same result
Char -- the `Show` instance of `TypeRep` just returns the name of the type
If you want Typeable to work for your own types, you can have the compiler generate an instance for you with the DeriveDataTypeable extension.
{-# LANGUAGE DeriveDataTypeable #-}
import Data.Typeable
data Person = Person { name :: String, age :: Int } deriving Typeable
You can also write your own instance, but really, no one has the time for that. Apparently you can't - see the comments
You can now use typeOf to grab a run-time representation of your type. We can query information about the type constructor (abbreviated to TyCon) and its type arguments:
-- (undefined :: Person) stands for "some value of type Person".
-- If you have a real Person you can use that too.
-- typeOf does not use the value, only the type
-- (which is known at compile-time; typeOf is dispatched using the normal instance selection rules)
ghci> typeOf (undefined :: Person)
Person
ghci> tyConName $ typeRepTyCon $ typeOf (undefined :: Person)
"Person"
ghci> tyConModule $ typeRepTyCon $ typeOf (undefined :: Person)
"Main"
Data.Typeable also provides a type-safe cast operation which allows you to branch on a value's runtime type, somewhat like C#'s as operator.
f :: Typeable a => a -> String
f x = case (cast x :: Maybe Int) of
Just i -> "I can treat i as an int in this branch " ++ show (i * i)
Nothing -> case (cast x :: Maybe Bool) of
Just b -> "I can treat b as a bool in this branch " ++ if b then "yes" else "no"
Nothing -> "x was of some type other than Int or Bool"
ghci> f True
"I can treat b as a bool in this branch yes"
ghci> f (3 :: Int)
"I can treat i as an int in this branch 9"
Incidentally, a nicer way to write f is to use a GADT enumerating the set of types you expect your function to be called with. This allows us to lose the Maybe (f can never fail!), does a better job of documenting our assumptions, and gives compile-time feedback when we need to change the set of admissible argument types for f. (You can write a class to make Admissible implicit if you like.)
data Admissible a where
AdInt :: Admissible Int
AdBool :: Admissible Bool
f :: Admissible a -> a -> String
f AdInt i = "I can treat i as an int in this branch " ++ show (i * i)
f AdBool b = "I can treat b as a bool in this branch " ++ if b then "yes" else "no"
In reality I probably wouldn't do either of these - I'd just stick f in a class and define instances for Int and Bool.
If you want run-time information about the right-hand side of a type definition, you need to use the entertainingly-named Data.Data, which defines a subclass of Typeable called Data.** GHC can derive Data for you too, with the same extension:
{-# LANGUAGE DeriveDataTypeable #-}
import Data.Typeable
import Data.Data
data Person = Person { name :: String, age :: Int } deriving (Typeable, Data)
Now we can grab a run-time representation of the values of a type, not just the type itself:
ghci> dataTypeOf (undefined :: Person)
DataType {tycon = "Main.Person", datarep = AlgRep [Person]}
ghci> dataTypeConstrs $ dataTypeOf (undefined :: Person)
[Person] -- Person only defines one constructor, called Person
ghci> constrFields $ head $ dataTypeConstrs $ dataTypeOf (undefined :: Person)
["name","age"]
Data.Data is the API for generic programming; if you ever hear people talking about "Scrap Your Boilerplate", this (along with Data.Generics, which builds on Data.Data) is what they mean. For example, you can write a function which converts record types to JSON using reflection on the type's fields.
toJSON :: Data a => a -> String
-- Implementation omitted because it is boring.
-- But you only have to write the boring code once,
-- and it'll be able to serialise any instance of `Data`.
-- It's a good exercise to try to write this function yourself!
* In recent versions of GHC, this API has changed somewhat. Consult the docs.
** Yes, the fully-qualified name of that class is Data.Data.Data.

Resources