autoapply alternatives and similar packages
Based on the "Template Haskell" category.
Alternatively, view autoapply alternatives based on common mentions on social networks and blogs.
-
haskell-src-meta
Maintenance of Matt Morrow's package for parsing haskell source to a TH AST. -
th-utilities
Collection of useful functions for use with Template Haskell -
th-desugar
Desugars Template Haskell abstract syntax to a simpler format without changing semantics -
th-lift-instances
Lift instances for common haskell data types -
th-extras
A grab bag of useful functions for use with Template Haskell -
language-haskell-extract
Useful functions using Template Haskell. -
th-instance-reification
Fixed versions of instances reification functions -
th-typegraph
Building graphs of the template haskell subtype relation -
th-env
Template Haskell splice that expands to an environment variable value
Access the most powerful time series database as a service
Do you think we are missing an alternative of autoapply or a related project?
README
autoapply
A Template-Haskell program to automatically pass arguments to functions wherever the type fits.
@tomjaguarpaw noticed that this is like "Type-directed implicit parameters".
TL;DR
You have the following values and want to stir them together and see what sticks.
foo :: Monad m => A -> B -> C -> m D
getA :: App A
myC :: C
$(autoApply [] ['getA, 'myC] 'foo)
will create
\b -> getA >>= \a -> foo a b myC
which has type B -> App D
or
autoApplyDecs reverse [] ['getA, 'myC] ['foo]
will create
oof :: B -> App D; oof b = do { a <- getA; foo a b myC }
Why to use it
One nice use-case is to avoiding writing boilerplate wrappers for using an API in your Monad stack. For instance imagine the following API.
data Instance; data ExtraOpenInfo; data Foo; data Bar; data Handle
openHandle :: MonadIO m => Instance -> Maybe ExtraOpenInfo -> m Handle
closeHandle :: MonadIO m => Instance -> Handle -> m ()
useHandle :: MonadIO m => Instance -> Handle -> Foo -> m Bar
You'd like to use this in your polysemy
application, using the Input
effect
to pass the Instance
handle around, and always passing Nothing
for
ExtraOpenInfo
because you don't use that functionality and getting a Foo
from some other constraint MyConstraint
. You define the following values.
myExtraOpenInfo :: Maybe ExtraOpenInfo
myExtraOpenInfo = Nothing
getInstance :: Member (Input Instance) r => Sem r Instance
getInstance = input
getFoo :: MyConstraint m => m Foo
getFoo = ...
You then create the wrapped API thusly:
autoapplyDecs
(<> "'") -- Function to transform the names of the wrapped functions
['myExtraOpenInfo, 'getInstance, 'getFoo]
-- Potential arguments to pass which must subsume the argument type of the
-- function.
[] -- Potential arguments to pass which must unify with the argument type
['openHandle, 'closeHandle, 'useHandle] -- Functions to wrap
Which creates the following declarations:
openHandle'
:: (Member (Input Instance) r, MonadIO (Sem r)) => Sem r Handle
closeHandle'
:: (Member (Input Instance) r, MonadIO (Sem r)) => Handle -> Sem r ()
useHandle'
:: (Member (Input Instance) r, MyConstraint (Sem r), MonadIO (Sem r))
=> Handle -> Sem r Bar
Notice:
Instance
is supplied with theMember (Input Instance) r
constraintFoo
is supplied byMyConstraint (Sem r)
ExtraOpenInfo
is not present at all, being supplied internally bymyExtraOpenInfo
To see the generated code (it's exactly what you'd expect) compile
test/Types.hs
with -ddump-splices
.
How to use this
To generate a new top-level declaration you'll need:
- The
Name
of a function to apply to some arguments. - The
Name
s of some values to try and pass as arguments- values whose type must subsume the argument type (if you're unsure, you probably want this one)
- values whose type must merely unify with the argument type
- A way of generating a name for this declaration given the wrapped name
:: String -> String
.
The new declaration will be generated, equal to the wrapped one but using the supplied arguments wherever possible.
Arguments can be used in two ways:
As regular parameters
- If the type of the argument matches directly
- An example is applying
takeWhile
tonot
;not
is passed as thea -> Bool
argument totakeWhile
.$(autoapply [] ['not] 'takeWhile) :: [Bool] -> [Bool]
Using a monadic bind
- If the wrapped function returns a value of type
m a
and there exists an instanceMonad m
- If the argument is of type
n a
and there exists an instanceMonad m
- If
m
unifies withn
- An example is applying
putStrLn
togetLine
. TheString
result ofgetLine
is passed toputStrLn
$(autoapply [] ['getLine] 'putStrLn) :: IO ()
- If the wrapped function returns a value of type
It's important to note that Monad
instance checking only goes as far as
template-haskell
's reifyInstances
. i.e. only the instance heads are
checked.
Constraints are checked (up to reifyInstances
) so it won't pass reverse
to
(+)
for example.
Monadic binds are performed in the order of arguments passed to the wrapped function, and will be performed more than once if the argument is used multiple times.
You may want to either type your generated declarations manually (putting the
type after the splice) or turn on -XNoMonomorphismRestriction
if your
arguments have polymorphic constraints.
Where to use it
In an expression context:
$(autoApply ['my, 'arguments] [] 'myFunction)
At the top level to generate several declarations
$(autoApplyDecs (funNameToNewFunName :: String -> String) ['my] ['arguments] ['myFunction, 'anotherFunction])
See also
This has a similar feel to some other programs which also generate Haskell expressions based on types.
There are a couple of differences here:
- One doesn't need to specify the desired type up front, this tool will just go as far as it can.
- This tool isn't doing any interesting proof search instead it's just "if it fits, I sits"