I have recently started playing around with liquid haskell, and from all of the tutorials I could find, I could not find any examples like the following.
data MaybePerson = MaybePerson {
name' :: Maybe String,
age' :: Maybe Int
}
data Person = Person {
name :: String,
age :: Int
}
{-# measure p :: MaybePerson -> Bool #-}
p (MaybePerson (Just _) (Just _)) = True
p _ = False
{-# type JustPerson = {x:MaybePerson | p x} #-}
-- Attempts to instantiate a maybe person into a concrete Person
{-# getPerson :: JustPerson -> Person #-}
getPerson (MaybePerson (Just name) (Just age)) = Person name age
getPerson _ = undefined
If I try the following, my module does not type-check, as expected:
test = getPerson (MaybePerson Nothing Nothing)
However, for some reason, the following still does not type check:
test2 = getPerson (MaybePerson (Just "bob") (Just 25))
and I get the error
Error: Liquid Type Mismatch
36 | test2 = getPerson (MaybePerson (Just "bob") (Just 25))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Inferred type
VV : {v : MaybePerson | v == ?a}
not a subtype of Required type
VV : {VV : MaybePerson | Blank.p VV}
In Context
?a : MaybePerson
Moreover, if I leave out the getPerson _ = undefined line, I get
Your function is not total: not all patterns are defined.
Even though clearly this function is total because of the precondition specified with liquidhaskell.
What am I doing wrong here? I essentially just want to be able to reason with subtypes of a Maybe a type which are coming from the Just constructor, but I couldn't find any examples anywhere of where to do this properly.
sorry for the late reply! I should find some way to get notified about questions. Ok, there are two things going on, both of which we should fix!
First, there is something glitchy happening with
{-# measure p :: MaybePerson -> Bool #-}
The right syntax is just
{-# measure p #-}
p :: MaybePerson -> Bool
But there was no error message, so there's no way for you to know!
Second, when I change the above I still get some strange error about
GHC.Maybe -- I can't recall the exact issue right now, will fix
on my laptop, but for illustration, I tweaked your code to:
{-# LIQUID "--exact-data-cons" #-}
import Prelude hiding (Maybe (..))
data Maybe a = Just a | Nothing
To redefine Maybe. This should not be needed will figure out a fix ASAP
With this, your code works as is, e.g. see here
http://goto.ucsd.edu/liquid/index.html#?demo=permalink%2F1573693313_399.hs
So you can now define
{-# getPerson :: JustPerson -> Person #-}
getPerson (MaybePerson (Just name) (Just age)) = Person name age
and just remove the equation for the other cases. Further,
test1 = getPerson (MaybePerson Nothing Nothing) -- error
yields a type error, but the below is safe
test2 = getPerson (MaybePerson (Just "bob") (Just 25)) -- ok
Thanks for pointing this out, will fix!
Related
I am trying to create a select query with a simple where-clause using Haskell's beam. From https://haskell-beam.github.io/beam/user-guide/queries/select/#where-clause, I believed that this would work:
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeSynonymInstances #-}
module Lib where
import Data.Int ( Int32 )
import Data.Word ( Word32 )
import Database.Beam
data FooT f
= Foo
{ _fooId :: Columnar f Int32
, _fooBar :: Columnar f Word32
}
deriving (Generic, Beamable)
instance Table FooT where
data PrimaryKey FooT f =
FooId (Columnar f Int32) deriving (Generic, Beamable)
primaryKey = FooId . _fooId
type Foo = FooT Identity
type FooId = PrimaryKey FooT Identity
deriving instance Show Foo
deriving instance Eq Foo
data BazDb f = BazDb
{ _bazFoos :: f (TableEntity FooT)
}
deriving (Generic, Database be)
bazDb :: DatabaseSettings be BazDb
bazDb = defaultDbSettings
selectFoosByBar :: HasQBuilder be => Word32 -> SqlSelect be Foo
selectFoosByBar bar = select $
filter_ (\foo -> _fooBar foo ==. bar) $
all_ $ _bazFoos bazDb
but I am missing some vital detail, so I get the following compile error:
<SNIP>/Lib.hs:42:22: error:
• Couldn't match type ‘QGenExpr QValueContext be QBaseScope Word32’
with ‘Word32’
Expected type: Word32
Actual type: Columnar (QExpr be QBaseScope) Word32
• In the first argument of ‘(==.)’, namely ‘_fooBar foo’
In the expression: _fooBar foo ==. bar
In the first argument of ‘filter_’, namely
‘(\ foo -> _fooBar foo ==. bar)’
• Relevant bindings include
foo :: FooT (QExpr be QBaseScope) (bound at src/Lib.hs:42:15)
selectFoosByBar :: Word32 -> SqlSelect be Foo
(bound at src/Lib.hs:41:1)
|
42 | filter_ (\foo -> _fooBar foo ==. bar) $
|
Now, the error message itself is quite clear, but what I can't quite figure out is which side of ==. I need to modify nor how to do it. Or if it's a matter of some missing extension or type annotation.
The relevant portions of the code
selectFoosByBar bar = select $
filter_ (\foo -> _fooBar foo ==. bar) $
all_ $ _bazFoos bazDb
If we look at ==. you will see (==.) :: SqlEq expr a => a -> a -> expr Bool, so both sides of ==. need to have the same type.
Now look at what is on the left of (==.) we see _fooBar foo :: Columnar f Word32. We can't get out of Columnar but we can make bar into something beam can work with using val_:
selectFoosByBar bar = select $
filter_ (\foo -> _fooBar foo ==. val_ bar) $
all_ $ _bazFoos bazDb
Notice this only work if we remove the type annotation. With a type annotation it will look like:
selectFoosByBar
:: (HasQBuilder be, HasSqlEqualityCheck be Word32,
HasSqlValueSyntax
(Sql92ExpressionValueSyntax
(Sql92SelectTableExpressionSyntax
(Sql92SelectSelectTableSyntax
(Sql92SelectSyntax (BeamSqlBackendSyntax be)))))
Word32) =>
Word32 -> SqlSelect be (FooT Identity)
selectFoosByBar bar = select $
filter_ (\foo -> _fooBar foo ==. val_ bar) $
all_ $ _bazFoos bazDb
I think it needs this huge annotation so ghc can keep track of the be abstracted out backend.
Edit:
If we enable ConstraintKinds we can simplify the annotation:
type MagicSql be =
HasSqlValueSyntax
(Sql92ExpressionValueSyntax
(Sql92SelectTableExpressionSyntax
(Sql92SelectSelectTableSyntax
(Sql92SelectSyntax (BeamSqlBackendSyntax be)))))
selectFoosByBar
:: (HasQBuilder be, HasSqlEqualityCheck be Word32, MagicSql be Word32) =>
Word32 -> SqlSelect be (FooT Identity)
On the offending line, bar :: Word32 (per the signature of selectFoosByBar).
I think _fooBar foo is a Columnar (something) Word32.
The error message says the problem is with the first arg to ==., but looking at the type of ==., I think you could change either side to get agreement.
Why is bar :: Word32? It makes intuitive sense; you're trying to filter by a word so the arg should be a word. That suggests that you probably want to do something to _fooBar foo to get a Word32 "out of" it. That might be a straightforward function, but more likely it's going to be the opposite: somehow lifting your ==. bar operation up into the "query expression" space.
Suppose you're writing some Template Haskell code that transforms record declarations. The first transformation you would want to write is the identity one, right? So let's go over the fields and not change them:
module TH where
import Language.Haskell.TH
import Language.Haskell.TH.Syntax
foo :: DecsQ -> DecsQ
foo = fmap $ map $ \d -> case d of
DataD _ dataName tvbs Nothing [con#(RecC conName fields)] [] ->
DataD [] dataName
tvbs
Nothing
[RecC conName $ map transformField fields]
[]
_ -> d
-- TODO: Write my awesome transformation here
transformField :: VarBangType -> VarBangType
transformField (v, b, t) = (v, b, t)
You try it out in a module with a record type:
{-# LANGUAGE TemplateHaskell #-}
import TH
foo [d| data R = MkR{ x :: Int } |]
So far so good. However, things break if we turn on DuplicateRecordFields in our program, even though we haven't written transformField yet (i.e. it is still the identity function):
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE DuplicateRecordFields #-}
import TH
foo [d| data R = MkR{ x :: Int } |]
This program now fails to compile, with the following message:
Use.hs:5:1: error:
Illegal variable name: $sel:x:MkR
When splicing a TH declaration:
data R_0 = MkR_1 {$sel:x:MkR_2 :: GHC.Types.Int}
Even this super-minimal program fails with a similar error:
{-# LANGUAGE DuplicateRecordFields, TemplateHaskell #-}
$([d| data R = MkR{ x :: Int } |])
There's not much mystery why this happens: as the DuplicateRecordFields documentation explains, field selectors are mangled to be unique accross all record types, and TH gets these mangled field names.
But how to solve this is not explained on that same page. For now, I am using the following function to unmangle a mangled field name into something that roundtrips accross data declaration splices:
import Data.List.Split
unmangle :: Name -> Name
unmangle (Name occ flavour) = Name occ' flavour
where
occ' = case wordsBy (== ':') (occString occ) of
["$sel", fd, _qual] -> mkOccName fd
_ -> occ
transformField :: VarBangType -> VarBangType
transformField (v, b, t) = (unmangle v, b, t)
This works, but doesn't feel like the right solution, and probably won't survive changes to GHC-internal details of name mangling. Is there a better way to do this?
With Template Haskell I would like to generate records, eg:
data MyRecordA = MyRecordA
{fooA :: String, barA :: Bool}
The uppercase A in MyRecordA, fooA, barA and the type Bool of the second field should be variable and specified by the caller of the TH function.
I tried with several variations of:
{-# LANGUAGE TemplateHaskell #-}
module THRecord where
import Language.Haskell.TH
mkRecord :: Name -> Name -> Q [Dec]
mkRecord name cls = [d|
data $typeName :: $constName
{$fieldFoo, $fieldBar}
|]
where
typeName = conT $ "MyRecord" <> name
constrName = RecC $ "MyRecord" <> name
fieldFoo = sigP name ($clsString)
fieldBar = sigP name cls
clsString = conT "String"
Unfortunately, I get parse errors like
src/THRecord.hs:8:9: error: parse error on input ‘$fieldFoo’
There are several issues here; lets look at them one by one. The splice you have:
[d|
data $typeName :: $constName
{$fieldFoo, $fieldBar}
|]
is simply not valid; you may only splice entire expressions, types, or declarations, and not parts thereof. You also probably meant data $typeName = $constName but of course the same restriction applies to that, so it still won't work.
The definition
fieldFoo = sigP name ($clsString)
doesn't work because you may not have an splice of a local variable without an intervening quote. This is known as the 'stage restriction'.
fieldFoo = sigP name ($clsString)
fieldBar = sigP name cls
sigP is wrong because it constructs a pattern; you don't need to build any patterns (not sure what you meant here).
typeName = conT $ "MyRecord" <> name
constrName = RecC $ "MyRecord" <> name
clsString = conT "String"
All of these are trying to treat a Name as a String. If it isn't clear why that doesn't make sense, perhaps you should familiarize yourself with the basics of Haskell.
Now the solution:
import Data.Monoid
import Language.Haskell.TH
import Language.Haskell.TH.Syntax
defBang = Bang NoSourceUnpackedness NoSourceStrictness
stringType = ConT ''String
mkRecord :: Name -> Name -> Q [Dec]
mkRecord name cls = (pure.pure)$
DataD [] typeName [] Nothing [constr] []
where
typeName = mkName $ "MyRecord" <> nameBase name
constr = RecC typeName [(mkName $ "foo" <> nameBase name, defBang, stringType)
,(mkName $ "bar" <> nameBase name, defBang, ConT cls)]
Note that you don't even make use of the Q monad here; not to generate names, nor to reify info about names. Therefore you can actually write a function Name -> Name -> Dec and then applying pure.pure to the result produces a type which can be spliced.
The above is for GHC 8.0.1; the AST of Template Haskell varies significantly between majour releases so it may not compile exactly as is on other versions.
Then e.g.
$(mkRecord (mkName "XYZ") ''Bool)
$(mkRecord (mkName "A") ''Int)
produces
data MyRecordXYZ = MyRecordXYZ {fooXYZ :: String, barXYZ :: Bool}
data MyRecordA = MyRecordA {fooA :: String, barA :: Int}
Finally, here is a solution which doesn't require TH. The family of types you wish to generate can be represented in a first class way:
import GHC.TypeLits
data MyRecord (nm :: Symbol) t = MyRecord { foo :: String, bar :: t }
type MyRecordA = MyRecord "A" Bool
type MyRecordXYZ = MyRecord "XYZ" Int
I've got a situation where I need to compile some Haskell code on different machines.
At least one of these machines has a rather old version of Control.Concurrent.STM, that doesn't know modifyTVar. My current workaround is to copy the code for modifyTVar from a newer version of the package. This got me wondering, if it would be possible to use template Haskell to check if a function is already defined and only define it, if it's missing. I'm aware that the proper solution would probably be to get more recent packages, but the situation got me curious.
It seems to be possible as follows. First a helper module:
{-# LANGUAGE TemplateHaskell #-}
module AddFn where
import Language.Haskell.TH
-- | Add a function if it doesn't exist.
addFn :: String -> Q [Dec] -> Q [Dec]
addFn name decl = do
r <- lookupValueName name
case r of
Just l -> return []
Nothing -> report False ("adding missing " ++ name) >> decl
and use it as in
{-# LANGUAGE TemplateHaskell #-}
module Main where
import AddFn
import qualified Data.Traversable as T
$(addFn "mapM"
[d| mapM :: (Monad m) => (a -> m b) -> [a] -> m [b]
mapM = T.mapM
|])
$(addFn "mapM1"
[d| mapM1 :: (Monad m) => (a -> m b) -> [a] -> m [b]
mapM1 = T.mapM
|])
The drawback is that it's using lookupValueName, which is only in the recent versions of TH, so when dealing with old installations, this probably won't help. Perhaps a possible solution would be to instead call reify on a given name, and use recover to handle the case when the name is missing.
Update: The version using reify instead of lookupValueName works:
-- | Add a function if it doesn't exist.
addFn :: String -> Q [Dec] -> Q [Dec]
addFn name decl = recover decl (reify (mkName name) >> return [])
Template Haskell is somewhat overkill for this - you can use CPP instead, using the MIN_VERSION macros that Cabal will define:
{-# LANGUAGE CPP #-}
#if MIN_VERSION_stm(2, 3, 0)
-- nothing
#else
modifyTVar = ...
#endif
Quasiquotation as described in haskellwiki is shown mostly as useful tool for embedding other languages inside Haskell without messing around with string quotation.
Question is: For Haskell itself, how easy it would be to put existing Haskell code through a quasiquoter for the purpose of just replacing tokens and passing the result over to ghc? Perhaps Template Haskell is key here?
I have looked for code examples and didn't find any. Some EDSLs can benefit from this ability by reducing the size of their combinating operators (e.g. turn 'a .|. b .>>. c' to '[myedsl|a | b >> c]').
You can build quasi-quoters that manipulate Haskell code by, for example, using the haskell-src-meta package. It parses valid Haskell code into an AST, which you can then modify.
In this case, the easiest way to modify the AST is by using Data.Generics to apply a generic transformation to the whole AST that replaces operators with other operators.
We'll begin by building the transformation function for generic Haskell expressions. The data type that represents an expression is Exp in the template-haskell package.
For example, to convert the operator >> to .>>. we'd use a function like
import Language.Haskell.TH (Exp(..), mkName)
replaceOp :: Exp -> Exp
replaceOp (VarE n) | n == mkName ">>" = VarE (mkName ".>>.")
replaceOp e = e
This changes a variable expression (VarE), but cannot do anything to any other kind of expressions.
Now, to walk the whole AST and to replace all occurrences of >> we'll use the functions everywhere and mkT from Data.Generic.
import Data.Generics (everywhere, mkT)
replaceEveryOp :: Exp -> Exp
replaceEveryOp = everywhere (mkT replaceOp)
In order to make several replacements, we can alter the function so that it takes an association list of any operator to replace.
type Replacements = [(String, String)]
replaceOps :: Replacements -> Exp -> Exp
replaceOps reps = everywhere (mkT f) where
f e#(VarE n) = case rep of
Just n' -> VarE (mkName n')
_ -> e
where rep = lookup (show n) reps
f e = e
And by the way, this is a good example of a function that is much nicer to write by using the view patterns language extension.
{-# LANGUAGE ViewPatterns #-}
replaceOps :: Replacements -> Exp -> Exp
replaceOps reps = everywhere (mkT f) where
f (VarE (replace -> Just n')) = VarE (mkName n')
f e = e
replace n = lookup (show n) reps
Now all that's left for us to do is to build the "myedsl" quasi-quoter.
{-# LANGUAGE ViewPatterns #-}
import Data.Generics (everywhere, mkT)
import Language.Haskell.Meta.Parse (parseExp)
import Language.Haskell.TH (Exp(..), mkName, ExpQ)
import Language.Haskell.TH.Quote (QuasiQuoter(..))
type Replacements = [(String, String)]
replacements :: Replacements
replacements =
[ ("||", ".|.")
, (">>", ".>>.")
]
myedls = QuasiQuoter
{ quoteExp = replaceOpsQ
, quotePat = undefined
, quoteType = undefined
, quoteDec = undefined
}
replaceOpsQ :: String -> ExpQ
replaceOpsQ s = case parseExp s of
Right e -> return $ replaceOps replacements e
Left err -> fail err
replaceOps :: Replacements -> Exp -> Exp
replaceOps reps = everywhere (mkT f) where
f (VarE (replace -> Just n')) = VarE (mkName n')
f e = e
replace n = lookup (show n) reps
If you save the above to its own module (e.g. MyEDSL.hs), then you can import it and use the quasi-quoter.
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE QuasiQuotes #-}
import MyEDSL
foo = [myedsl| a || b >> c |]
Note that I've used || instead of | because the latter is not a valid operator in Haskell (since it's the syntactic element used for pattern guards).