content: {-# 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)
  