applicative-splice (empty) → 0.0.0.0
raw patch · 4 files changed
+343/−0 lines, 4 filesdep +basedep +haskell-src-extsdep +haskell-src-metasetup-changed
Dependencies added: base, haskell-src-exts, haskell-src-meta, mtl, syb, template-haskell
Files
- Control/Applicative/Splice.hs +184/−0
- LICENSE +121/−0
- Setup.hs +2/−0
- applicative-splice.cabal +36/−0
+ Control/Applicative/Splice.hs view
@@ -0,0 +1,184 @@+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE DeriveFunctor #-}+{-# LANGUAGE DisambiguateRecordFields #-}+{-# LANGUAGE ViewPatterns #-}+{-# LANGUAGE FlexibleContexts #-}++-- | This module defines a quasiquoter for making it easier to program with+-- applicatives and monads.+--+-- = Basic usage+--+-- The 'appl' quasiquoter takes almost any Haskell expression, and expands to the+-- same Haskell expression, wrapped in a call to 'pure'. For example,+--+-- @+-- [appl| 4 + 5 |]+-- -- expands to:+-- pure (4 + 5)+-- @+--+-- Additionally, the expression given to 'appl' may contain "splices". A splice+-- is a dollar sign immediately followed by a parenthesized expression.+-- The parentheses can be dropped if the expression consists of a single+-- identifier. Here are examples of splices.+--+-- @+-- $(f a b)+-- $([0..7])+-- $getLine+-- @+--+-- The syntax for splices are stolen from Template Haskell+-- splices. Therefore whitespaces are not allowed after the dollar sign.+--+-- The expression inside a splice should have the type @f a@ for some+-- 'Applicative' @f@ and some type @a@. The splice itself should be+-- placed in a context where a value of type @a@ is expected. Then 'appl' expands+-- to an applicative expression that "embeds" the result of the applicative+-- action in the pure context. For example,+--+-- @+-- [appl| f $x (4 + $y) |]+-- -- expands to:+-- (\\a b -> f a (4 + b)) \<$> x \<*> y+-- @+--+-- In terms of types, the dollar sign is like a function of type @forall a. f a -> a@,+-- although it is not a real function.+--+-- The type of the 'appl' expression will be in the same applicative as the+-- splices. This also means mutliple splices in the same block must+-- share the same applicative.+--+-- = Special case: functor splices+--+-- When an 'appl' block contains exactly one splice, the type of the expression+-- inside the splice can be @f a@ for any functor @f@, and it doesn't have to be+-- an applicative. The expansion will only contain a call to '<$>', not 'pure'+-- or '<*>'.+--+-- @+-- [appl| (\"Length\", length $m) |]+-- -- expands to:+-- (\\a -> (\"Length\", length a)) \<$> m+-- @+--+-- = Nested splices for monads+--+-- When your applicative is also a monad, you can have splices inside another+-- splice. For example,+--+-- @+-- [appl| $(putStrLn $ "Hello, " ++ $getLine) |]+-- -- expands to:+-- getLine >>= (\\a -> putStrLn $ "Hello, " ++ a)+-- @+--+-- As in this case, no call to '<$>' is generated if the whole 'appl' block+-- consists of a single splice.+module Control.Applicative.Splice+ ( appl+ ) where++import Control.Monad.State+import Control.Monad.Writer+import Data.Generics+import qualified Language.Haskell.Exts as H+import qualified Language.Haskell.Meta as Meta+import qualified Language.Haskell.TH as TH+import qualified Language.Haskell.TH.Quote as TH++data ApplicativeExp var exp+ = AdoJoin [(var, ApplicativeExp var exp)] exp+ -- ado { v0 <- join e0; v1 <- join e1; ...; return eN }+ deriving (Functor)++-- | The applicative-splice quasiquoter.+appl :: TH.QuasiQuoter+appl = TH.QuasiQuoter+ { quoteExp = genExp+ , quotePat = err+ , quoteType = err+ , quoteDec = err+ }+ where+ err _ = fail "This quasiquoter can only be used in an expression"++genExp :: String -> TH.ExpQ+genExp str = do+ e <- case H.parseExpWithMode parseMode str of+ H.ParseOk e -> return e+ H.ParseFailed loc msg -> fail $ show loc ++ ": " ++ msg+ unApplicative $ bimapApplicativeExp Meta.toName Meta.toExp $ quote e+ where+ parseMode = H.defaultParseMode+ { H.extensions = map H.EnableExtension [H.TemplateHaskell]+ }++bimapApplicativeExp+ :: (a -> c)+ -> (b -> d)+ -> ApplicativeExp a b+ -> ApplicativeExp c d+bimapApplicativeExp f g = go+ where+ go (AdoJoin binds body) = AdoJoin (map goBind binds) (g body)+ goBind (var, app) = (f var, go app)++quote :: H.Exp -> ApplicativeExp H.Name H.Exp+quote e0 = evalState (go e0) 0+ where+ go e = do+ (body, binds) <- runWriterT (everywhereM' (mkM onSplice) e)+ return $ AdoJoin binds body++ onSplice (H.SpliceExp splice) = do+ v <- gensym+ e' <- lift $ go e+ tell [(v, e')]+ return $ H.Var $ H.UnQual v+ where+ !e = case splice of+ H.IdSplice str -> H.Var $ H.UnQual $ H.Ident str+ H.ParenSplice x -> x+ onSplice e = return e++ gensym = do+ n <- get+ put $! n + 1+ return $ argVar n++argVar :: Int -> H.Name+argVar = H.Ident . ("applicative_splice_" ++) . show++unApplicative :: ApplicativeExp TH.Name TH.Exp -> TH.ExpQ+unApplicative (AdoJoin (unzip -> (vars, args)) body) = case args of+ [] -> [| pure $(pure body) |]+ first:rest -> foldl mkAp (join $ mkFmap <$> fun <*> unJoin first) rest+ where+ fun = TH.lamE (map TH.varP vars) (pure body)+ mkAp f a = [| $f <*> $(unJoin a) |]+ mkFmap f a+ | isTrivialIdentity f = pure a+ | otherwise = [| $(pure f) <$> $(pure a) |]+ isTrivialIdentity (TH.LamE [TH.VarP v] e) = e == TH.VarE v+ isTrivialIdentity _ = False++unJoin :: ApplicativeExp TH.Name TH.Exp -> TH.ExpQ+unJoin (AdoJoin binds body) = foldr bind (pure body) binds+ where+ bind (v, a) b = [| $(unJoin a) >>= \ $(TH.varP v) -> $b |]++everywhereM' :: (Monad m) => GenericM m -> GenericM m+everywhereM' f x = do + x' <- f x+ gmapM (everywhereM' f) x'++_view :: TH.ExpQ -> TH.ExpQ+_view eq = do+ e <- eq+ let str = TH.pprint e+ [| putStrLn str |]
+ LICENSE view
@@ -0,0 +1,121 @@+Creative Commons Legal Code++CC0 1.0 Universal++ CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE+ LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN+ ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS+ INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES+ REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS+ PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM+ THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED+ HEREUNDER.++Statement of Purpose++The laws of most jurisdictions throughout the world automatically confer+exclusive Copyright and Related Rights (defined below) upon the creator+and subsequent owner(s) (each and all, an "owner") of an original work of+authorship and/or a database (each, a "Work").++Certain owners wish to permanently relinquish those rights to a Work for+the purpose of contributing to a commons of creative, cultural and+scientific works ("Commons") that the public can reliably and without fear+of later claims of infringement build upon, modify, incorporate in other+works, reuse and redistribute as freely as possible in any form whatsoever+and for any purposes, including without limitation commercial purposes.+These owners may contribute to the Commons to promote the ideal of a free+culture and the further production of creative, cultural and scientific+works, or to gain reputation or greater distribution for their Work in+part through the use and efforts of others.++For these and/or other purposes and motivations, and without any+expectation of additional consideration or compensation, the person+associating CC0 with a Work (the "Affirmer"), to the extent that he or she+is an owner of Copyright and Related Rights in the Work, voluntarily+elects to apply CC0 to the Work and publicly distribute the Work under its+terms, with knowledge of his or her Copyright and Related Rights in the+Work and the meaning and intended legal effect of CC0 on those rights.++1. Copyright and Related Rights. A Work made available under CC0 may be+protected by copyright and related or neighboring rights ("Copyright and+Related Rights"). Copyright and Related Rights include, but are not+limited to, the following:++ i. the right to reproduce, adapt, distribute, perform, display,+ communicate, and translate a Work;+ ii. moral rights retained by the original author(s) and/or performer(s);+iii. publicity and privacy rights pertaining to a person's image or+ likeness depicted in a Work;+ iv. rights protecting against unfair competition in regards to a Work,+ subject to the limitations in paragraph 4(a), below;+ v. rights protecting the extraction, dissemination, use and reuse of data+ in a Work;+ vi. database rights (such as those arising under Directive 96/9/EC of the+ European Parliament and of the Council of 11 March 1996 on the legal+ protection of databases, and under any national implementation+ thereof, including any amended or successor version of such+ directive); and+vii. other similar, equivalent or corresponding rights throughout the+ world based on applicable law or treaty, and any national+ implementations thereof.++2. Waiver. To the greatest extent permitted by, but not in contravention+of, applicable law, Affirmer hereby overtly, fully, permanently,+irrevocably and unconditionally waives, abandons, and surrenders all of+Affirmer's Copyright and Related Rights and associated claims and causes+of action, whether now known or unknown (including existing as well as+future claims and causes of action), in the Work (i) in all territories+worldwide, (ii) for the maximum duration provided by applicable law or+treaty (including future time extensions), (iii) in any current or future+medium and for any number of copies, and (iv) for any purpose whatsoever,+including without limitation commercial, advertising or promotional+purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each+member of the public at large and to the detriment of Affirmer's heirs and+successors, fully intending that such Waiver shall not be subject to+revocation, rescission, cancellation, termination, or any other legal or+equitable action to disrupt the quiet enjoyment of the Work by the public+as contemplated by Affirmer's express Statement of Purpose.++3. Public License Fallback. Should any part of the Waiver for any reason+be judged legally invalid or ineffective under applicable law, then the+Waiver shall be preserved to the maximum extent permitted taking into+account Affirmer's express Statement of Purpose. In addition, to the+extent the Waiver is so judged Affirmer hereby grants to each affected+person a royalty-free, non transferable, non sublicensable, non exclusive,+irrevocable and unconditional license to exercise Affirmer's Copyright and+Related Rights in the Work (i) in all territories worldwide, (ii) for the+maximum duration provided by applicable law or treaty (including future+time extensions), (iii) in any current or future medium and for any number+of copies, and (iv) for any purpose whatsoever, including without+limitation commercial, advertising or promotional purposes (the+"License"). The License shall be deemed effective as of the date CC0 was+applied by Affirmer to the Work. Should any part of the License for any+reason be judged legally invalid or ineffective under applicable law, such+partial invalidity or ineffectiveness shall not invalidate the remainder+of the License, and in such case Affirmer hereby affirms that he or she+will not (i) exercise any of his or her remaining Copyright and Related+Rights in the Work or (ii) assert any associated claims and causes of+action with respect to the Work, in either case contrary to Affirmer's+express Statement of Purpose.++4. Limitations and Disclaimers.++ a. No trademark or patent rights held by Affirmer are waived, abandoned,+ surrendered, licensed or otherwise affected by this document.+ b. Affirmer offers the Work as-is and makes no representations or+ warranties of any kind concerning the Work, express, implied,+ statutory or otherwise, including without limitation warranties of+ title, merchantability, fitness for a particular purpose, non+ infringement, or the absence of latent or other defects, accuracy, or+ the present or absence of errors, whether or not discoverable, all to+ the greatest extent permissible under applicable law.+ c. Affirmer disclaims responsibility for clearing rights of other persons+ that may apply to the Work or any use thereof, including without+ limitation any person's Copyright and Related Rights in the Work.+ Further, Affirmer disclaims responsibility for obtaining any necessary+ consents, permissions or other rights required for any use of the+ Work.+ d. Affirmer understands and acknowledges that Creative Commons is not a+ party to this document and has no duty or obligation with respect to+ this CC0 or use of the Work.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ applicative-splice.cabal view
@@ -0,0 +1,36 @@+-- Initial applicative-splice.cabal generated by cabal init. For further +-- documentation, see http://haskell.org/cabal/users-guide/++name: applicative-splice+version: 0.0.0.0+synopsis: Write applicative programs in direct style (generalizes idiom brackets).+description: This package defines a quasiquoter that lets you write+ your monadic/applicative programs in direct style, i.e.+ the style in which you would write it in a+ language with side effects. Unlike actual side effects,+ this does not affect purity of the langauge, because it's+ just a syntactic sugar around the usual (\<$>), (\<*>) and+ (>>=) functions.+homepage: https://github.com/takano-akio/applicative-splice+license: PublicDomain+license-file: LICENSE+author: Takano Akio+maintainer: aljee@hyper.cx+-- copyright: +category: Control+build-type: Simple+-- extra-source-files: +cabal-version: >=1.10++source-repository head+ type: git+ location: https://github.com/takano-akio/applicative-splices.git++library+ exposed-modules: Control.Applicative.Splice+ -- other-modules: + -- other-extensions: + build-depends: base >=4.8 && <5, template-haskell, haskell-src-meta,+ haskell-src-exts, mtl, syb+ -- hs-source-dirs: + default-language: Haskell2010