{-# LANGUAGE LambdaCase #-} {- | Instead of converting explicitly between 'Expr's and 'MultiLet', it might be nicer to use a pattern synonym: > pattern MultiLet' :: NonEmpty (Binding s a) -> Expr s a -> Expr s a > pattern MultiLet' as b <- (multiLetFromExpr -> Just (MultiLet as b)) where > MultiLet' as b = wrapInLets as b > > multiLetFromExpr :: Expr s a -> Maybe (MultiLet s a) > multiLetFromExpr = \case > Let x mA a b -> Just (multiLet x mA a b) > _ -> Nothing This works in principle, but GHC as of v8.8.1 doesn't handle it well: https://gitlab.haskell.org/ghc/ghc/issues/17096 This should be fixed by GHC-8.10, so it might be worth revisiting then. -} module Dhall.Syntax.MultiLet ( MultiLet(..) , multiLet , wrapInLets ) where import Data.List.NonEmpty (NonEmpty (..)) import Dhall.Syntax.Binding (Binding) import Dhall.Syntax.Expr (Expr (..)) import qualified Data.List.NonEmpty as NonEmpty {-| Generate a 'MultiLet' from the contents of a 'Let'. In the resulting @'MultiLet' bs e@, @e@ is guaranteed not to be a 'Let', but it might be a @('Note' … ('Let' …))@. Given parser output, 'multiLet' consolidates @let@s that formed a let-block in the original source. -} multiLet :: Binding s a -> Expr s a -> MultiLet s a multiLet b0 = \case Let b1 e1 -> let MultiLet bs e = multiLet b1 e1 in MultiLet (NonEmpty.cons b0 bs) e e -> MultiLet (b0 :| []) e {-| Wrap let-'Binding's around an 'Expr'. 'wrapInLets' can be understood as an inverse for 'multiLet': > let MultiLet bs e1 = multiLet b e0 > > wrapInLets bs e1 == Let b e0 -} wrapInLets :: Foldable f => f (Binding s a) -> Expr s a -> Expr s a wrapInLets bs e = foldr Let e bs {-| This type represents 1 or more nested `Let` bindings that have been coalesced together for ease of manipulation -} data MultiLet s a = MultiLet (NonEmpty (Binding s a)) (Expr s a)