packages feed

lorentz-0.15.0: src/Lorentz/Lambda.hs

-- SPDX-FileCopyrightText: 2022 Oxhead Alpha
-- SPDX-License-Identifier: LicenseRef-MIT-OA

module Lorentz.Lambda
  ( WrappedLambda(..)
  , Lambda
  , mkLambda
  , mkLambdaRec
  ) where

import Data.Singletons (demote)

import Lorentz.Annotation
import Lorentz.Base
import Lorentz.Coercions
import Lorentz.Instr.Framed
import Lorentz.Value
import Lorentz.Zip
import Morley.AsRPC
import Morley.Michelson.Doc
import Morley.Michelson.Typed hiding (Contract, pattern S)
import Morley.Michelson.Typed.Contract (giveNotInView)
import Morley.Michelson.Untyped (noAnn)
import Morley.Util.Markdown
import Morley.Util.Type

-- | A helper type to construct Lorentz lambda values; Use this for lambda
-- values outside of Lorentz contracts or with @push@.
data WrappedLambda i o
  = WrappedLambda (i :-> o)
  | RecLambda (i ++ '[WrappedLambda i o] :-> o)
  deriving stock (Show, Eq, Generic)

instance (KnownList i, ZipInstr i, ZipInstr o) => IsoValue (WrappedLambda i o) where
  type ToT (WrappedLambda i o) = 'TLambda (ToT (ZippedStack i)) (ToT (ZippedStack o))
  toVal (WrappedLambda i) = mkVLam $ unLorentzInstr $ zippingStack i
  toVal (RecLambda i) = mkVLamRec $ unLorentzInstr $
    framed @'[WrappedLambda i o] (unzipInstr @i)
    ## i
    ## zipInstr
  fromVal (VLam (LambdaCode i)) = WrappedLambda $ unzippingStack $ LorentzInstr i
  fromVal (VLam (LambdaCodeRec i)) = RecLambda $
    framed @'[WrappedLambda i o] (zipInstr @i)
    ## LorentzInstr i
    ## unzipInstr

instance MapLorentzInstr (WrappedLambda inp out) where
  mapLorentzInstr
    :: (forall i o. (i :-> o) -> (i :-> o))
    -> WrappedLambda inp out
    -> WrappedLambda inp out
  mapLorentzInstr f = \case
    WrappedLambda i -> WrappedLambda $ f i
    RecLambda i -> RecLambda $ f i

instance (Each '[HasAnnotation] '[ZippedStack i, ZippedStack o])
  => HasAnnotation (WrappedLambda i o) where
  getAnnotation b = NTLambda noAnn
    (getAnnotation @(ZippedStack i) b)
    (getAnnotation @(ZippedStack o) b)

instance HasRPCRepr (WrappedLambda i o) where type AsRPC (WrappedLambda i o) = WrappedLambda i o

-- | A constructor providing the required constraint for 'WrappedLambda'. This is
-- the only way to construct a lambda that uses operations forbidden in views.
mkLambda :: (IsNotInView => i :-> o) -> WrappedLambda i o
mkLambda i = WrappedLambda $ giveNotInView i

-- | A constructor providing the required constraint for 'WrappedLambda'. This is
-- the only way to construct a lambda that uses operations forbidden in views.
mkLambdaRec :: (IsNotInView => i ++ '[WrappedLambda i o] :-> o) -> WrappedLambda i o
mkLambdaRec i = RecLambda $ giveNotInView i

-- | A type synonym representing Michelson lambdas.
type Lambda i o = WrappedLambda '[i] '[o]

instance (Each [Typeable, ReifyList TypeHasDoc] [i, o])
  => TypeHasDoc (WrappedLambda i o) where
  typeDocName _ = "WrappedLambda (extended lambda)"
  typeDocMdReference tp wp =
    let DocItemRef ctorDocItemId = docItemRef (DType tp)
        refToThis = mdLocalRef (mdTicked "WrappedLambda") ctorDocItemId
    in applyWithinParens wp $
      mconcat $ intersperse " " [refToThis, refToStack @i, refToStack @o]
    where
    refToStack :: forall s. ReifyList TypeHasDoc s => Markdown
    refToStack =
      let stack = reifyList @_ @TypeHasDoc @s (\p -> typeDocMdReference p (WithinParens False))
      in mconcat
          [ mdBold "["
          , case stack of
              [] -> " "
              st -> mconcat $ intersperse (mdBold "," <> " ") st
          , mdBold "]"
          ]

  typeDocMdDescription =
    "`WrappedLambda i o` stands for a sequence of instructions which accepts stack \
    \of type `i` and returns stack of type `o`.\n\n\
    \When both `i` and `o` are of length 1, this primitive corresponds to \
    \the Michelson lambda. In more complex cases code is surrounded with `pair`\
    \and `unpair` instructions until fits into mentioned restriction.\
    \"
  typeDocDependencies _ = mconcat
    [ reifyList @_ @TypeHasDoc @i dTypeDepP
    , reifyList @_ @TypeHasDoc @o dTypeDepP
    , [ dTypeDep @Integer
      , dTypeDep @Natural
      , dTypeDep @MText
      ]
    ]
  typeDocHaskellRep _ _ = Nothing
  typeDocMichelsonRep _ =
    ( Just "WrappedLambda [Integer, Natural, MText, ()] [ByteString]"
    , demote @(ToT (WrappedLambda [Integer, Natural, MText, ()] '[ByteString]))
    )

instance ( CanCastTo (ZippedStack inp1) (ZippedStack inp2)
         , CanCastTo (ZippedStack out1) (ZippedStack out2)
         , CanCastTo (ZippedStack (inp1 ++ '[WrappedLambda inp1 out1]))
                     (ZippedStack (inp2 ++ '[WrappedLambda inp2 out2]))
         )
  => WrappedLambda inp1 out1 `CanCastTo` WrappedLambda inp2 out2 where
  castDummy = castDummyG