packages feed

lorentz 0.15.2 → 0.16.0

raw patch · 24 files changed

+1067/−97 lines, 24 files

Files

CHANGES.md view
@@ -1,6 +1,23 @@ <!-- Unreleased: append new entries here -->  +0.16.0+======+* [!1370](https://gitlab.com/morley-framework/morley/-/merge_requests/1370)+  Add `OverloadedRecordDot` support for Lorentz `StoreClass`+  + Deprecated `StoreClass.Extra`, as it does more or less the same thing as+    `OverloadedRecordDot`, but a lot worse.+* [!1369](https://gitlab.com/morley-framework/morley/-/merge_requests/1369)+  Add `IsLabel` and `Default` instances for `EntrypointRef`+  + Now `OverloadedLabels` can be used instead of `Call`+  + `def` from `Data.Default` can be used instead of `CallDefault`+* [!1364](https://gitlab.com/morley-framework/morley/-/merge_requests/1364)+  Update to LTS-21.4 (GHC 9.4.5)+* [!1358](https://gitlab.com/morley-framework/morley/-/merge_requests/1358)+  Make missing `Generic` errors more readable+* [!1367](https://gitlab.com/morley-framework/morley/-/merge_requests/1367)+  Add Range type to Lorentz, see `Lorentz.Range` module documentation+ 0.15.2 ====== * [!1362](https://gitlab.com/morley-framework/morley/-/merge_requests/1362)
README.md view
@@ -1,6 +1,6 @@-> :warning: **Note: this project is being deprecated.**+> :warning: **Note: this project is deprecated.** >-> It will no longer be maintained after the activation of protocol "N" on the Tezos mainnet.+> It is no longer maintained since the activation of protocol "Nairobi" on the Tezos mainnet (June 24th, 2023).  # Morley Lorentz EDSL 
lorentz.cabal view
@@ -5,7 +5,7 @@ -- see: https://github.com/sol/hpack  name:           lorentz-version:        0.15.2+version:        0.16.0 synopsis:       EDSL for the Michelson Language description:    Lorentz is a powerful meta-programming tool which allows one to write Michelson contracts directly in Haskell. It has the same instructions as Michelson, but operates on Haskell values and allows one to use Haskell features. category:       Language@@ -71,6 +71,7 @@       Lorentz.Polymorphic       Lorentz.Prelude       Lorentz.Print+      Lorentz.Range       Lorentz.Rebound       Lorentz.Referenced       Lorentz.ReferencedByName@@ -142,7 +143,7 @@       UndecidableInstances       UndecidableSuperClasses       ViewPatterns-  ghc-options: -Weverything -Wno-missing-exported-signatures -Wno-missing-import-lists -Wno-missed-specialisations -Wno-all-missed-specialisations -Wno-unsafe -Wno-safe -Wno-missing-local-signatures -Wno-monomorphism-restriction -Wno-implicit-prelude -Wno-prepositive-qualified-module -Wno-missing-safe-haskell-mode+  ghc-options: -Weverything -Wno-missing-exported-signatures -Wno-missing-import-lists -Wno-missed-specialisations -Wno-all-missed-specialisations -Wno-unsafe -Wno-safe -Wno-missing-local-signatures -Wno-monomorphism-restriction -Wno-implicit-prelude -Wno-prepositive-qualified-module -Wno-missing-safe-haskell-mode -Wno-missing-kind-signatures -Wno-implicit-lift -Wno-unticked-promoted-constructors   build-depends:       aeson-pretty     , base-noprelude >=4.7 && <5
src/Lorentz.hs view
@@ -30,6 +30,7 @@ import Lorentz.Polymorphic as Exports import Lorentz.Prelude as Exports import Lorentz.Print as Exports+import Lorentz.Range as Exports import Lorentz.Rebound as Exports import Lorentz.Referenced as Exports import Lorentz.ReferencedByName as Exports
src/Lorentz/ADT.hs view
@@ -47,7 +47,6 @@   ) where  import Data.Vinyl.Core (RMap(..), Rec(..))-import GHC.Generics qualified as G import GHC.TypeLits (AppendSymbol, Symbol)  import Lorentz.Base@@ -55,6 +54,7 @@ import Lorentz.Constraints import Morley.Michelson.Typed.Haskell.Instr import Morley.Michelson.Typed.Haskell.Value+import Morley.Util.Generic import Morley.Util.Label (Label) import Morley.Util.Named import Morley.Util.Type (type (++))@@ -321,14 +321,30 @@ constructStack =   I (instrConstructStack @dt @(ToTs fields)) --- | Decompose a complex object into its fields------ >>> deconstruct @TestProduct # constructStack @TestProduct -$ testProduct--- TestProduct {fieldA = True, fieldB = 42, fieldC = ()}+{- | Decompose a complex object into its fields++>>> deconstruct @TestProduct # constructStack @TestProduct -$ testProduct+TestProduct {fieldA = True, fieldB = 42, fieldC = ()}++Will show human-readable error on a missing `Generic` instance:++>>> data Foo = Foo () ()+>>> deconstruct @Foo+...+... GHC.Generics.Rep Foo+... is stuck. Likely+... Generic Foo+... instance is missing or out of scope.+...++>>> data Foo = Foo () () deriving (Generic, IsoValue)+>>> pretty $ deconstruct @Foo+[UNPAIR, DIP {  }]+-} deconstruct   :: forall dt fields st .   ( InstrDeconstructC dt (ToTs st)-  , fields ~ GFieldTypes (G.Rep dt) '[]+  , fields ~ GFieldTypes (GRep dt) '[]   , ToTs fields ++ ToTs st ~ ToTs (fields ++ st)   )   => dt : st :-> (fields ++ st)
src/Lorentz/Annotation.hs view
@@ -36,10 +36,15 @@ import Morley.Tezos.Address import Morley.Tezos.Core import Morley.Tezos.Crypto+import Morley.Util.Generic import Morley.Util.Named import Morley.Util.Text import Morley.Util.TypeLits +{- $setup+>>> import Lorentz+-}+ ---------------------------------------------------------------------------- -- Annotation Customization ----------------------------------------------------------------------------@@ -79,31 +84,47 @@ -- | Use this in the instance of @HasAnnotation@ when field annotations -- should not be generated. gGetAnnotationNoField-    :: forall a. (GHasAnnotation (G.Rep a), GValueType (G.Rep a) ~ ToT a)+    :: forall a. (GHasAnnotation (GRep a), GValueType (GRep a) ~ ToT a)     => FollowEntrypointFlag -> Notes (ToT a) gGetAnnotationNoField =-  \_ -> gGetAnnotation @(G.Rep a) def NotFollowEntrypoint NotGenerateFieldAnn ^. _1+  \_ -> gGetAnnotation @(GRep a) def NotFollowEntrypoint NotGenerateFieldAnn ^. _1 --- | This class defines the type and field annotations for a given type. Right now--- the type annotations come from names in a named field, and field annotations are--- generated from the record fields.+{- | This class defines the type and field annotations for a given type. Right+now the type annotations come from names in a named field, and field annotations+are generated from the record fields.++Allows generic derivation. The type must have @Generic@ and @IsoValue@+instances.++>>> data Foo = Foo+>>> instance HasAnnotation Foo+...+... GHC.Generics.Rep Foo+... is stuck. Likely+... Generic Foo+... instance is missing or out of scope.+...++>>> data Foo = Foo deriving (Generic, IsoValue)+>>> instance HasAnnotation Foo+-} class HasAnnotation a where   getAnnotation :: FollowEntrypointFlag -> Notes (ToT a)   default getAnnotation-    :: ( GHasAnnotation (G.Rep a), GValueType (G.Rep a) ~ ToT a-       , GIsSumType (G.Rep a), TypeHasFieldNamingStrategy a+    :: ( GHasAnnotation (GRep a), GValueType (GRep a) ~ ToT a+       , GIsSumType (GRep a), TypeHasFieldNamingStrategy a        )     => FollowEntrypointFlag     -> Notes (ToT a)   getAnnotation b =-    gGetAnnotation @(G.Rep a) (fromMaybe (gAnnOptions @a) $ annOptions @a) b GenerateFieldAnn ^. _1+    gGetAnnotation @(GRep a) (fromMaybe (gAnnOptions @a) $ annOptions @a) b GenerateFieldAnn ^. _1    annOptions :: Maybe AnnOptions   annOptions = Nothing -gAnnOptions :: forall a. (GIsSumType (G.Rep a), TypeHasFieldNamingStrategy a) => AnnOptions+gAnnOptions :: forall a. (GIsSumType (GRep a), TypeHasFieldNamingStrategy a) => AnnOptions gAnnOptions-  | gIsSumType @(G.Rep a) = def+  | gIsSumType @(GRep a) = def   | otherwise = def { fieldAnnModifier = typeFieldNamingStrategy @a }  instance (HasAnnotation a, KnownSymbol name)
src/Lorentz/Base.hs view
@@ -89,7 +89,7 @@ >>> import Lorentz >>> import Prelude () >>> :{-some_code :: KnownValue a => s :-> Lambda a () : s+some_code :: KnownValue a => s :-> ('[a] :-> '[()]) : s some_code = Lorentz.do   push Lorentz.do     drop
src/Lorentz/Coercions.hs view
@@ -46,6 +46,7 @@ import Lorentz.Wrappable import Lorentz.Zip import Morley.Michelson.Typed+import Morley.Util.Generic import Morley.Util.Named  ----------------------------------------------------------------------------@@ -219,14 +220,30 @@          , CanCastTo e1 e2, CanCastTo f1 f2 ) =>          CanCastTo (a1, b1, c1, d1, e1, f1) (a2, b2, c2, d2, e2, f2) --- | Implementation of 'castDummy' for types composed from smaller types.--- It helps to ensure that all necessary constraints are requested in instance--- head.+{- | Implementation of 'castDummy' for types composed from smaller types.+It helps to ensure that all necessary constraints are requested in instance+head.++Shows human-readable errors when @up@ is stuck.++>>> data Foo = Foo+>>> castDummyG (Proxy @Foo) (Proxy @())+...+... GHC.Generics.Rep Foo+... is stuck. Likely+... Generic Foo+... instance is missing or out of scope.+...++>>> data Foo = Foo deriving Generic+>>> castDummyG (Proxy @Foo) (Proxy @())+()+-} castDummyG-  :: (Generic a, Generic b, GCanCastTo (G.Rep a) (G.Rep b))+  :: (Generic a, Generic b, GCanCastTo (GRep a) (GRep b))   => Proxy a -> Proxy b -> () castDummyG (_ :: Proxy a) (_ :: Proxy b) = ()-  where _dummy = Dict @(Generic a, Generic b, GCanCastTo (G.Rep a) (G.Rep b))+  where _dummy = Dict @(Generic a, Generic b, GCanCastTo (GRep a) (GRep b))  type family GCanCastTo x y :: Constraint where   GCanCastTo (G.M1 _ _ x) (G.M1 _ _ y) = GCanCastTo x y
src/Lorentz/Constraints/Scopes.hs view
@@ -87,9 +87,9 @@ >>> epAddressToContract ... ... Can't check if type-... ToT p+... ToT p0 ... contains `operation` or nested `big_map`s. Perhaps you need to add-... NiceParameter p+... NiceParameter p0 ... constraint? You can also try adding a type annotation. ... -}@@ -105,12 +105,12 @@  Shows human-readable errors on ambiguity: ->>> push 1+>>> push undefined ... ... Can't check if type-... ToT t+... ToT t0 ... contains `operation`, `big_map`, `contract`, `ticket` or `sapling_state`. Perhaps you need to add-... NiceConstant t+... NiceConstant t0 ... constraint? You can also try adding a type annotation. ... @@ -136,9 +136,9 @@ >>> dup ... ... Can't check if type-... ToT a+... ToT a0 ... contains `ticket`. Perhaps you need to add-... Dupable a+... Dupable a0 ... constraint? You can also try adding a type annotation. ... @@ -164,9 +164,9 @@ >>> pack ... ... Can't check if type-... ToT a+... ToT a0 ... contains `operation`, `big_map`, `ticket` or `sapling_state`. Perhaps you need to add-... NicePackedValue a+... NicePackedValue a0 ... constraint? You can also try adding a type annotation. ... -}@@ -189,9 +189,9 @@ >>> unpack ... ... Can't check if type-... ToT a+... ToT a0 ... contains `operation`, `big_map`, `contract`, `ticket` or `sapling_state`. Perhaps you need to add-... NiceUnpackedValue a+... NiceUnpackedValue a0 ... constraint? You can also try adding a type annotation. ... -}@@ -219,9 +219,9 @@ >>> view' @"SomeView" ... ... Can't check if type-... ToT ret+... ToT ret0 ... contains `operation`, `big_map` or `ticket`. Perhaps you need to add-... NiceViewable ret+... NiceViewable ret0 ... constraint? You can also try adding a type annotation. ... -}@@ -241,9 +241,9 @@ >>> emptySet ... ... Can't check if type-... ToT e+... ToT e0 ... contains non-comparable types. Perhaps you need to add-... NiceComparable e+... NiceComparable e0 ... constraint? You can also try adding a type annotation. ... -}@@ -266,9 +266,9 @@ >>> emptyBigMap @Integer ... ... Can't check if type-... ToT v+... ToT v0 ... contains `big_map`. Perhaps you need to add-... NiceNoBigMap v+... NiceNoBigMap v0 ... constraint? You can also try adding a type annotation. ... -}
src/Lorentz/CustomArith/RationalArith.hs view
@@ -40,7 +40,7 @@   , pairToRational   ) where -import Prelude (Either(..), Eq, Integer, Maybe, Natural, Show, Text, show, unsafe, ($), (++), (==))+import Prelude (Either(..), Eq, Integer, Maybe, Natural, Show, Text, show, unsafe, ($), (++), (==), type (~)) import Text.Show qualified  import Lorentz.Annotation
src/Lorentz/Entrypoints/Core.hs view
@@ -45,13 +45,18 @@   , RequireAllUniqueEntrypoints'   ) where +import Data.Default (Default(..))+import Data.Type.Bool (type (&&))+import Data.Type.Equality ((:~:)(..)) import Data.Typeable (typeRep) import Data.Vinyl (Rec(..)) import Fcf (Eval, Exp) import Fcf qualified import Fcf.Utils qualified as Fcf import Fmt (pretty)-import Type.Errors (DelayError, DelayErrorFcf, IfStuck)+import GHC.TypeLits (CharToNat, ConsSymbol, NatToChar, UnconsSymbol, type (-), type (<=?))+import Type.Errors (DelayError, IfStuck)+import Unsafe.Coerce (unsafeCoerce)  import Morley.Michelson.Typed import Morley.Michelson.Untyped qualified as U@@ -366,12 +371,35 @@   -- | Call the given entrypoint; calling default is not treated specially.   -- You have to provide entrypoint name via passing it as type argument.   ---  -- Unfortunatelly, here we cannot accept a label because in most cases our+  -- Unfortunately, here we cannot accept a label because in most cases our   -- entrypoints begin from capital letter (being derived from constructor name),   -- while labels must start from a lower-case letter, and there is no way to   -- make a conversion at type-level.   Call :: NiceEntrypointName name => EntrypointRef ('Just name) +type UppercaseFirstChar :: Symbol -> Symbol+type family UppercaseFirstChar s where+  UppercaseFirstChar s = TryUppercase (UnconsSymbol s)++type TryUppercase :: Maybe (Char, Symbol) -> Symbol+type family TryUppercase mb where+  TryUppercase 'Nothing = ""+  TryUppercase ('Just '(c, s)) = UppercaseChar c `ConsSymbol` s++type UppercaseChar :: Char -> Char+type family UppercaseChar c where+  UppercaseChar c =+    If ('a' <=? c && c <=? 'z')+      (NatToChar (CharToNat c - 0x20))+      c++instance (NiceEntrypointName (UppercaseFirstChar name), mname ~ 'Just (UppercaseFirstChar name))+  => IsLabel name (EntrypointRef mname) where+  fromLabel = Call++instance mname ~ 'Nothing => Default (EntrypointRef mname) where+  def = CallDefault+ -- | Constraint on type-level entrypoint name specifier. type NiceEntrypointName name = (KnownSymbol name, ForbidDefaultName name) @@ -480,30 +508,24 @@   'Text "  " ':<>: 'ShowType constraint ':$$:   'Text "constraint" -type CheckStuckEp cp mname fixedArg freeArg =-  -- NB: if you're wondering why we need @freeArg@ and @fixedArg@, when-  -- @fixedArg@ is fully polymorphic, GHC will assign it to the stuck type-  -- family, and @ShowType@ will show FCF internals, which is exactly what we-  -- wanted to avoid. Hence if @fixedArg@ is also stuck, we show @freeArg@-  -- instead. Finally, when there are no errors, we equate @freeArg@ and-  -- @fixedArg@.+type CheckStuckEp cp mname arg =   IfStuck (GetEntrypointArgCustom cp mname)-    (IfStuck fixedArg-      (DelayError (StuckEpErrorTemplate cp-        (HasEntrypointArg cp (EntrypointRef mname) freeArg)-      ))-      (DelayErrorFcf (StuckEpErrorTemplate cp-        (HasEntrypointArg cp (EntrypointRef mname) fixedArg)-      ))-    )-    (Fcf.Pure (freeArg ~ fixedArg))+    (DelayError (StuckEpErrorTemplate cp+      (HasEntrypointArg cp (EntrypointRef mname) arg)+    ))+    (Fcf.Pure (GetEntrypointArgCustom cp mname ~ arg))  instance-  ( CheckStuckEp cp mname arg' arg-  , GetEntrypointArgCustom cp mname ~ arg'+  ( CheckStuckEp cp mname arg   , ParameterDeclaresEntrypoints cp) =>-  HasEntrypointArg cp (EntrypointRef mname) arg' where-  useHasEntrypointArg epRef =+  HasEntrypointArg cp (EntrypointRef mname) arg where+  useHasEntrypointArg epRef+    -- this ugliness is the best I could come up with... GHC 9.4 shows equality+    -- constraint errors before custom type errors apparently, hence we can't+    -- put the equality into instance head. But unless we got a custom error, we+    -- have the equality, so it's perfectly safe to unsafeCoerce here.+    -- -- @lierdakil+    | Refl :: GetEntrypointArgCustom cp mname :~: arg <- unsafeCoerce Refl =     case parameterEntrypointCallCustom @cp epRef of       EntrypointCall{} -> (Dict, eprName epRef) 
src/Lorentz/Entrypoints/Doc.hs view
@@ -73,6 +73,7 @@ import Morley.Michelson.Typed.Haskell.Doc import Morley.Michelson.Typed.Haskell.Instr import Morley.Michelson.Untyped qualified as Untyped+import Morley.Util.Generic import Morley.Util.Label (Label) import Morley.Util.Markdown import Morley.Util.Type@@ -483,12 +484,12 @@   => Rec (CaseClauseL inp out) (CaseClauses a)   -> Rec (CaseClauseL inp out) (CaseClauses a) documentEntrypoints r =-  gDocumentEntrypoints @(BuildEPTree' a) @kind @(G.Rep a) @'[]+  gDocumentEntrypoints @(BuildEPTree' a) @kind @(GRep a) @'[]     (ParamBuilder id) r (\RNil -> RNil)  -- | Constraint for 'documentEntrypoints'. type DocumentEntrypoints kind a =-  (Generic a, GDocumentEntrypoints (BuildEPTree' a) kind (G.Rep a) '[])+  (Generic a, GDocumentEntrypoints (BuildEPTree' a) kind (GRep a) '[])  type BuildEPTree' a = BuildEPTree (GetParameterEpDerivation a) a @@ -530,7 +531,6 @@  instance {-# OVERLAPPABLE #-}          ( 'CaseClauseParam ctor cf ~ GCaseBranchInput ctor x-         , KnownSymbol ctor          , DocItem (DEntrypoint (EntrypointKindOverride kind))          , DeriveCtorFieldDoc ctor cf          ) =>@@ -556,7 +556,6 @@  instance {-# OVERLAPPABLE #-}          ( 'CaseClauseParam ctor cf ~ GCaseBranchInput ctor x-         , KnownSymbol ctor          , DeriveCtorFieldDoc ctor cf          , T.SingI heps          ) =>@@ -669,7 +668,7 @@       | areFinalizedParamBuildingSteps pbs =           error "Applying finalization second time"       | otherwise =-          fromMaybe [PbsUncallable pbs] . listToMaybe . catMaybes $+          fromMaybe [PbsUncallable pbs] . safeHead . catMaybes $           epDescs <&> \epDesc -> tryShortcut epDesc pbs      -- Further we check whether given 'EpCallingStep's form prefix of
src/Lorentz/Entrypoints/Impl.hs view
@@ -28,6 +28,7 @@ import Morley.Michelson.Typed.Haskell.Value (GValueType) import Morley.Michelson.Untyped (FieldAnn, mkAnnotation, noAnn) import Morley.Util.Fcf (Over2, TyEqSing, type (<|>))+import Morley.Util.Generic import Morley.Util.Type  import Lorentz.Annotation@@ -159,7 +160,7 @@     -- ^ We reached complex parameter part and will need to ask how to process it.  -- | Build 'EPTree' by parameter type.-type BuildEPTree mode a = GBuildEntrypointsTree mode (G.Rep a)+type BuildEPTree mode a = GBuildEntrypointsTree mode (GRep a)  type family GBuildEntrypointsTree (mode :: Type) (x :: Type -> Type)              :: EPTree where@@ -191,14 +192,14 @@  -- | Traverses sum type and constructs 'Notes' which report -- constructor names via field annotations.-type EntrypointsNotes mode ep a = (Generic a, GEntrypointsNotes mode ep (G.Rep a))+type EntrypointsNotes mode ep a = (Generic a, GEntrypointsNotes mode ep (GRep a))  -- | Makes up notes with proper field annotations for given parameter. mkEntrypointsNotes   :: forall mode ep a.       (EntrypointsNotes mode ep a, GenericIsoValue a, HasCallStack)   => Notes (ToT a)-mkEntrypointsNotes = fst $ gMkEntrypointsNotes @mode @ep @(G.Rep a)+mkEntrypointsNotes = fst $ gMkEntrypointsNotes @mode @ep @(GRep a)  -- | Makes up a way to lift entrypoint argument to full parameter. mkEpLiftSequence@@ -208,20 +209,20 @@       )   => Label name   -> EpConstructionRes (ToT a) (Eval (LookupEntrypoint mode ep a name))-mkEpLiftSequence = gMkEpLiftSequence @mode @ep @(G.Rep a)+mkEpLiftSequence = gMkEpLiftSequence @mode @ep @(GRep a)  -- | Makes up descriptions of entrypoints calling. mkEpDescs   :: forall mode ep a.       (EntrypointsNotes mode ep a)   => Rec EpCallingDesc (AllEntrypoints mode ep a)-mkEpDescs = gMkDescs @mode @ep @(G.Rep a)+mkEpDescs = gMkDescs @mode @ep @(GRep a)  -- | Fetches information about all entrypoints - leaves of 'Or' tree.-type AllEntrypoints mode ep a = GAllEntrypoints mode ep (G.Rep a)+type AllEntrypoints mode ep a = GAllEntrypoints mode ep (GRep a)  -- | Fetches information about all entrypoints - leaves of 'Or' tree.-type LookupEntrypoint mode ep a = GLookupEntrypoint mode ep (G.Rep a)+type LookupEntrypoint mode ep a = GLookupEntrypoint mode ep (GRep a)  -- | Generic traversal for 'EntrypointsNotes'. class GEntrypointsNotes (mode :: Type) (ep :: EPTree) (x :: Type -> Type) where
src/Lorentz/Expr.hs view
@@ -95,7 +95,7 @@   , viewE   ) where -import Prelude (HasCallStack, foldr, uncurry, ($))+import Prelude (HasCallStack, foldr, uncurry, ($), type (~))  import Lorentz.Arith import Lorentz.Base
src/Lorentz/Extensible.hs view
@@ -70,6 +70,7 @@ import Lorentz.Pack import Morley.AsRPC (HasRPCRepr(..)) import Morley.Michelson.Typed+import Morley.Util.Generic import Morley.Util.Label (Label) import Morley.Util.Markdown import Morley.Util.Type@@ -82,8 +83,8 @@ instance HasRPCRepr (Extensible x) where   type AsRPC (Extensible x) = Extensible x -type ExtVal x = (Generic x, GExtVal x (G.Rep x))-type GetCtors x = GGetCtors (G.Rep x)+type ExtVal x = (NiceGeneric x, GExtVal x (GRep x))+type GetCtors x = GGetCtors (GRep x)  -- | Converts a value from a Haskell representation to its --   extensible Michelson representation (i.e. (Natural, Bytestring) pair).@@ -226,7 +227,7 @@   -- | Implementation for 'typeDocDependencies' of the corresponding @Extensible@.   extensibleDocDependencies :: Proxy x -> [SomeDocDefinitionItem]   default extensibleDocDependencies-    :: (Generic x, GTypeHasDoc (G.Rep x))+    :: (Generic x, GTypeHasDoc (GRep x))     => Proxy x -> [SomeDocDefinitionItem]   extensibleDocDependencies = genericTypeDocDependencies 
src/Lorentz/Instr.hs view
@@ -348,7 +348,7 @@          => s :-> Map k v : s emptyMap = I EMPTY_MAP -emptyBigMap :: (NiceComparable k, KnownValue v, NiceNoBigMap v)+emptyBigMap :: (NiceComparable k, NiceNoBigMap v)             => s :-> BigMap k v : s emptyBigMap = I EMPTY_BIG_MAP 
src/Lorentz/Layouts/NonDupable.hs view
@@ -33,7 +33,7 @@ --    non-dupable elements are rare and so are supposed to be updated --    less often. ----- Unfortunatelly, there seems to be no decent way to see whether a type is+-- Unfortunately, there seems to be no decent way to see whether a type is -- dupable or not at TH time, so we have to accept some information explicitly. -- To ensure that this information is up to date, each time calling this -- function __we encourage the user to also add a test__ using utilities from
src/Lorentz/Macro.hs view
@@ -399,7 +399,7 @@ -- ... -- ... error: -- ... Couldn't match type: '[]--- ...                with: a0 : o'0+-- ...                with: Integer : o'0 -- ... -- -- When the number of elements is larger than the size of the stack, @framedN@
src/Lorentz/Prelude.hs view
@@ -21,6 +21,7 @@   , undefined   , error   , (!)+  , type (~)   ) where  import Named ((!))
+ src/Lorentz/Range.hs view
@@ -0,0 +1,799 @@+-- SPDX-FileCopyrightText: 2023 Oxhead Alpha+-- SPDX-License-Identifier: LicenseRef-MIT-OA++{-# LANGUAGE FunctionalDependencies, RoleAnnotations #-}++-- | Range type+module Lorentz.Range+  ( RangeBoundaryInclusion(..)+  , Range(..)+  , RangeIE(..)+  , RangeEI(..)+  , RangeEE(..)+  , OnRangeAssertFailure(..)+  , RangeFailureInfo(..)+  , RangeBoundary+  , NiceRange+  , CanCheckEmpty+  , mkRange+  , mkRange_+  , mkRangeFor+  , mkRangeFor_+  , mkRangeForSafe_+  , fromRange_+  , toRange_+  , rangeLower+  , rangeUpper+  , rangeLower_+  , rangeUpper_+  , inRange+  , inRange_+  , inRangeCmp+  , inRangeCmp_+  , assertInRange_+  , assertInRangeSimple_+  , isRangeEmpty+  , isRangeEmpty_+  , assertRangeNonEmpty_+  , mkOnRangeAssertFailureSimple+  , customErrorORAF+  , customErrorORAFSimple+  -- * Internals+  , isInclusive+  ) where++import Data.Coerce (coerce)+import Data.Constraint (Bottom(..))+import Data.Singletons (demote)+import Fmt (Buildable(..))+import GHC.TypeLits (ErrorMessage(..), TypeError)++import Lorentz.ADT as L+import Lorentz.Annotation as L+import Lorentz.Arith as L+import Lorentz.Base as L+import Lorentz.Coercions as L+import Lorentz.Constraints.Scopes as L+import Lorentz.Doc as L+import Lorentz.Errors as L+import Lorentz.Instr as L+import Lorentz.Macro as L+import Lorentz.Value+import Morley.Michelson.Typed qualified as T+import Morley.Michelson.Untyped (noAnn, pattern UnsafeAnnotation, typeAnnQ, unAnnotation)+import Morley.Util.Interpolate (i)+import Morley.Util.Named++{- $setup+>>> import Fmt (pretty)+>>> import Lorentz+>>> import Morley.Util.Named+-}++-- | Whether to include boundary in 'Range'+data RangeBoundaryInclusion = IncludeBoundary | ExcludeBoundary++-- | Helper class for 'RangeBoundaryInclusion'.+type RangeBoundary :: RangeBoundaryInclusion -> Constraint+class (DefaultToInclusive r, Typeable r) => RangeBoundary r where+  -- | Parentheses for the 'Buildable' instance+  rangeParens :: (Text, Text)+  -- | Haskell comparison which either includes or excludes the boundary+  inRangeComp :: Ord a => a -> a -> Bool+  -- | Lorentz comparison which either includes or excludes the boundary+  inRangeComp_ :: NiceComparable a => a : a : s :-> Bool : s+  -- | Human-readable name for the tag+  rangeName :: Text+  -- | Value-level representation as 'Bool'+  isInclusive :: Bool++instance RangeBoundary 'IncludeBoundary where+  rangeParens = ("[", "]")+  inRangeComp = (<=)+  inRangeComp_ = L.ge+  rangeName = "Inclusive"+  isInclusive = True++instance RangeBoundary 'ExcludeBoundary where+  rangeParens = ("(", ")")+  inRangeComp = (<)+  inRangeComp_ = L.gt+  rangeName = "Exclusive"+  isInclusive = False++-- | Hacky typeclass to default boundaries to 'IncludeBoundary'.+class DefaultToInclusive bound+instance DefaultToInclusive 'IncludeBoundary+instance DefaultToInclusive 'ExcludeBoundary+instance {-# incoherent #-} a ~ 'IncludeBoundary => DefaultToInclusive a++{- | The main range type. This exists as a base for other (new)types like+'Range', 'RangeEE', etc.++The zoo of 'Range' types exists mostly for the purpose of making errors less+scary:++>>> f = push (1 :: Integer, 2 :: Natural) # unpair # inRange_+...+... error:+... Couldn't match type ‘Range Natural’ with ‘Integer’+...++>>> mkRange_ @_ @_ @_ @Integer # add -$ #min :! 123 ::: #max :! 321 ::: 555+...+... error:+... No instance for (ArithOpHs+...   Morley.Michelson.Typed.Arith.Add (Range Integer) Integer ())+...+-}+type Range' :: RangeBoundaryInclusion -> RangeBoundaryInclusion -> Type -> Type+data Range' lower upper a = Range' { rangeLower' :: a, rangeUpper' :: a }+  deriving stock (Generic, Show, Eq)+  deriving anyclass IsoValue+type role Range' nominal nominal _++instance (HasAnnotation a, RangeBoundary lower, RangeBoundary upper)+  => HasAnnotation (Range' lower upper a) where+  getAnnotation f =+    let aann = getAnnotation @a f+        e :: forall x. RangeBoundary x => Text+        e | isInclusive @x = "i"+          | otherwise = "e"+        lann = UnsafeAnnotation $ "l_" <> e @lower+        uann = UnsafeAnnotation $ "u_" <> e @upper+    in T.NTPair [typeAnnQ|range|] lann uann noAnn noAnn aann aann++-- | Default range, inclusive in both boundaries.+newtype Range a = MkRangeII (Range' 'IncludeBoundary 'IncludeBoundary a)+  deriving newtype (TypeHasDoc, Generic, Show, Buildable, HasAnnotation, Eq)+  deriving anyclass IsoValue++-- | Range exclusive in both boundaries.+newtype RangeEE a = MkRangeEE (Range' 'ExcludeBoundary 'ExcludeBoundary a)+  deriving newtype (TypeHasDoc, Generic, Show, Buildable, HasAnnotation, Eq)+  deriving anyclass IsoValue++-- | Range inclusive in lower boundary, but exclusive in upper boundary.+newtype RangeIE a = MkRangeIE (Range' 'IncludeBoundary 'ExcludeBoundary a)+  deriving newtype (TypeHasDoc, Generic, Show, Buildable, HasAnnotation, Eq)+  deriving anyclass IsoValue++-- | Range exclusive in lower boundary, but inclusive in upper boundary.+newtype RangeEI a = MkRangeEI (Range' 'ExcludeBoundary 'IncludeBoundary a)+  deriving newtype (TypeHasDoc, Generic, Show, Buildable, HasAnnotation, Eq)+  deriving anyclass IsoValue++-- We have to export data constructors because otherwise Coercible complains in+-- some contexts, but those are not intended to be used by the end-users.+{-# WARNING MkRangeII, MkRangeEE, MkRangeEI, MkRangeIE+  "Avoid using data constructors directly, use mkRange instead."+  #-}++instance (TypeHasDoc a, RangeBoundary lower, RangeBoundary upper)+  => TypeHasDoc (Range' lower upper a) where+  typeDocName _ = case (isInclusive @lower, isInclusive @upper) of+    (True, True) -> "Range"+    (True, False) -> "RangeIE"+    (False, True) -> "RangeEI"+    (False, False) -> "RangeEE"+  typeDocMdDescription =+    "Signifies a range, which is " <> build (rangeName @lower) <> " wrt its lower boundary and "+    <> build (rangeName @upper) <> " wrt its upper boundary."+  typeDocMdReference pa =+    customTypeDocMdReference+      (typeDocName pa, DType pa) [DType (Proxy @a)]+  typeDocHaskellRep pa _ = Just $+    ( Just $ fromString . toString $ typeDocName pa <> " Integer"+    , [T.ConstructorRep (typeDocName pa) Nothing fields]+    )+    where+      fields = case getAnnotation @(Range' lower upper Integer) NotFollowEntrypoint of+        T.NTPair _ lower upper _ _ _ _ ->+          [ T.FieldRep (Just $ unAnnotation lower) (Just "Lower boundary") intRef+          , T.FieldRep (Just $ unAnnotation upper) (Just "Upper boundary") intRef+          ]+      intRef = SomeTypeWithDoc $ Proxy @Integer+  typeDocMichelsonRep pa =+    ( Just $ fromString . toString $ typeDocName pa <> " Integer"+    , demote @(ToT (Range' lower upper Integer))+    )++{- | Pretty-printer for Range.++>>> pretty $ mkRange 123 456+[123, 456]++>>> pretty $ mkRange @RangeEE 123 456+(123, 456)++>>> pretty $ mkRange @RangeIE 123 456+[123, 456)++>>> pretty $ mkRange @_ @'IncludeBoundary @'ExcludeBoundary 123 456+[123, 456)+-}+instance (RangeBoundary lower, RangeBoundary upper, Buildable a)+  => Buildable (Range' lower upper a) where+  build (Range' l r) = [i|#{lpar}#{l}, #{r}#{rpar}|]+    where lpar = fst $ rangeParens @lower+          rpar = snd $ rangeParens @upper++-- | Helper class for the 'Range' types that encapsulates common features.+class (RangeBoundary lower, RangeBoundary upper, ToT (range a) ~ ToT (a, a), KnownValue (range a)+  , Coercible range (Range' lower upper))+  => NiceRange range lower upper a | range -> lower upper, lower upper -> range++instance KnownValue a => NiceRange Range 'IncludeBoundary 'IncludeBoundary a+instance KnownValue a => NiceRange RangeIE 'IncludeBoundary 'ExcludeBoundary a+instance KnownValue a => NiceRange RangeEI 'ExcludeBoundary 'IncludeBoundary a+instance KnownValue a => NiceRange RangeEE 'ExcludeBoundary 'ExcludeBoundary a++-- | Get a range's lower bound+rangeLower :: forall range lower upper a. NiceRange range lower upper a => range a -> a+rangeLower = coerce $ rangeLower' @lower @upper @a++-- | Get a range's upper bound.+rangeUpper :: forall range lower upper a. NiceRange range lower upper a => range a -> a+rangeUpper = coerce $ rangeUpper' @lower @upper @a++{- | Construct a 'Range'. Bear in mind, no checks are performed, so this can+construct empty ranges.++You can control whether boundaries are included or excluded by choosing the+appropriate type out of 'Range', 'RangeIE', 'RangeEI' and 'RangeEE', either via+a type application or a type annotation. Alternatively, you can set @lower@ and+@upper@ type arguments to 'IncludeBoundary' or 'ExcludeBoundary'. If+unspecified, the default is to include both boundaries.++>>> pretty $ mkRange 123 456+[123, 456]++>>> pretty $ mkRange 123 123+[123, 123]++>>> pretty $ mkRange 123 0 -- note this range is empty!+[123, 0]++>>> pretty (mkRange 123 456 :: RangeEE Integer)+(123, 456)++>>> pretty $ mkRange @RangeIE 123 456+[123, 456)++>>> pretty $ mkRange @_ @'IncludeBoundary @'ExcludeBoundary 123 456+[123, 456)++>>> pretty $ mkRange @RangeEI 123 456+(123, 456]++>>> pretty $ mkRange @_ @'ExcludeBoundary @'IncludeBoundary 123 456+(123, 456]+-}+mkRange :: forall range lower upper a. NiceRange range lower upper a => a -> a -> range a+mkRange = coerce $ Range' @lower @upper @a++{- | Convert a 'Range' to a pair of @(lower, upper)@++>>> mkRangeForSafe_ # fromRange_ -$ #start :! (123 :: Integer) ::: #length :! (456 :: Natural)+(123,579)+-}+fromRange_ :: NiceRange range lower upper a => range a : s :-> (a, a) : s+fromRange_ = forcedCoerce_++{- | Convert a pair of @(lower, upper)@ to 'Range'. This has the potential to+construct an empty range.++>>> pretty (toRange_ -$ (123, 456) :: Range Integer)+[123, 456]++>>> pretty (toRange_ -$ (123, -100500) :: Range Integer) -- empty range+[123, -100500]+-}+toRange_ :: NiceRange range lower upper a => (a, a) : s :-> range a : s+toRange_ = forcedCoerce_++{- | Get the lower bound of a 'Range'++>>> mkRange_ # rangeLower_ -$ #min :! (123 :: Integer) ::: #max :! (456 :: Integer)+123+-}+rangeLower_ :: NiceRange range lower upper a => range a : s :-> a : s+rangeLower_ = fromRange_ # car++{- | Get the upper bound of a 'Range'++>>> mkRange_ # rangeUpper_ -$ #min :! (123 :: Integer) ::: #max :! (456 :: Integer)+456+-}+rangeUpper_ :: NiceRange range lower upper a => range a : s :-> a : s+rangeUpper_ = fromRange_ # cdr++{- | Construct a range by specifying the start and length instead of lower and+upper bounds. @lower = start@, and @upper = start + length@.++Note this can still construct empty ranges if @length@ is negative.++See 'mkRange' for the information on how to control boundary inclusion.++>>> pretty $ mkRangeFor 123 123+[123, 246]++>>> pretty $ mkRangeFor 123 (-123) -- empty range+[123, 0]+-}+mkRangeFor :: (NiceRange range lower upper a, Num a) => a -> a -> range a+mkRangeFor start len = mkRange start (start + len)++{- | The Lorentz version of 'mkRange'++>>> pretty $ mkRange_ -$ (#min :! (123 :: Integer)) ::: (#max :! (123 :: Integer))+[123, 123]++>>> pretty $ mkRange_ @RangeEE -$ (#min :! (123 :: Integer)) ::: (#max :! (123 :: Integer))+(123, 123)++Notice the latter range is empty by construction.+-}+mkRange_+  :: NiceRange range lower upper a+  => ("min" :! a) : ("max" :! a) : s :-> range a : s+mkRange_ = fromNamed #min # dip (fromNamed #max) # L.pair # toRange_++{- | The Lorentz version of 'mkRangeFor'. Note that @length@ can be of different+type from @start@.++>>> pretty $ mkRangeFor_ -$ (#start :! (123 :: Integer)) ::: (#length :! (123 :: Natural))+[123, 246]+-}+mkRangeFor_+  :: ( NiceRange range lower upper a+     , Dupable a+     , ArithOpHs T.Add a b a+     )+  => ("start" :! a) : ("length" :! b) : s :-> range a : s+mkRangeFor_+  = fromNamed #start+  # L.dup+  # L.dip+    ( L.dip (fromNamed #length)+    # L.add+    )+  # L.pair+  # toRange_++{- | A version of 'mkRangeFor_' that requires @length@ to be isomorphic to a+@nat@ and the range to be inclusive in both bounds. This guarantees that the+range is non-empty.++>>> pretty $ mkRangeForSafe_ -$ (#start :! (123 :: Integer)) ::: (#length :! (1 :: Natural))+[123, 124]+-}+mkRangeForSafe_+  :: forall a b s.+     ( Dupable a+     , ArithOpHs T.Add a b a+     , ToT b ~ 'T.TNat+     )+  => ("start" :! a) : ("length" :! b) : s :-> Range a : s+mkRangeForSafe_ = mkRangeFor_+  where+    _ = T.Dict @(ToT b ~ 'T.TNat)++{- | Check if a value is in range.++>>> let range = mkRange -2 2+>>> inRange range <$> [-4..4]+[False,False,True,True,True,True,True,False,False]+-}+inRange :: (NiceRange range lower upper a, Ord a) => range a -> a -> Bool+inRange = (== EQ) ... inRangeCmp++{- | Check if a value is in range, and return 'Ordering'. This function returns+'EQ' if the value is in range, 'LT' if it underflows, and 'GT if it overflows.++>>> inRangeCmp (mkRange -2 2) <$> [-4..4]+[LT,LT,EQ,EQ,EQ,EQ,EQ,GT,GT]++>>> inRangeCmp (mkRange @RangeIE -2 2) <$> [-4..4]+[LT,LT,EQ,EQ,EQ,EQ,GT,GT,GT]+-}+inRangeCmp+  :: forall range lower upper a. (NiceRange range lower upper a, Ord a)+  => range a -> a -> Ordering+inRangeCmp r x+  | Prelude.not $ inRangeComp @lower (rangeLower r) x = LT+  | Prelude.not $ inRangeComp @upper x (rangeUpper r) = GT+  | otherwise = EQ++{- | Lorentz version of 'inRange'.++>>> range = mkRange 0 10 :: RangeEE Integer+>>> testCode = push range # inRange_+>>> testCode -$ 123+False+>>> testCode -$ 10+False+>>> testCode -$ 0+False+>>> testCode -$ -123+False+>>> testCode -$ 1+True+>>> testCode -$ 5+True+-}+inRange_+  :: forall range lower upper a s. (NiceRange range lower upper a, NiceComparable a, Dupable a)+  => range a : a : s :-> Bool : s+inRange_+  = fromRange_+  # L.unpair+  # L.dupN @3+  # inRangeComp_ @lower+  # L.dug @2+  # inRangeComp_ @upper+  # L.and++{- | Lorentz version of 'inRangeCmp'. Instead of 'Ordering', returns an+'Integer', @-1@ if the value underflows, @0@ if it's in range and @1@ if it+overflows.++Use 'inRange_' if you don't particularly care whether the range is under- or+overflowed.++>>> range = mkRange 0 10 :: Range Integer+>>> testCode = push range # inRangeCmp_+>>> testCode -$ 123+1+>>> testCode -$ -123+-1+>>> testCode -$ 5+0++>>> push (mkRange 0 10 :: RangeIE Integer) # inRangeCmp_ -$ 0+0+>>> push (mkRange 0 10 :: RangeIE Integer) # inRangeCmp_ -$ 10+1++>>> push (mkRange 0 10 :: RangeEI Integer) # inRangeCmp_ -$ 0+-1+>>> push (mkRange 0 10 :: RangeEI Integer) # inRangeCmp_ -$ 10+0++>>> push (mkRange 0 10 :: RangeEE Integer) # inRangeCmp_ -$ 0+-1+>>> push (mkRange 0 10 :: RangeEE Integer) # inRangeCmp_ -$ 10+1++When lower and upper boundaries are the same and at least one boundary is+exclusive, it is ambiguous whether the value on the boundary overflows or+underflows. In this case, lower boundary takes precedence if it's exclusive.++>>> push (mkRange 0 0 :: RangeIE Integer) # inRangeCmp_ -$ 0+1++>>> push (mkRange 0 0 :: RangeEI Integer) # inRangeCmp_ -$ 0+-1++>>> push (mkRange 0 0 :: RangeEE Integer) # inRangeCmp_ -$ 0+-1+-}+inRangeCmp_+  :: forall range lower upper a s. (NiceRange range lower upper a, NiceComparable a, Dupable a)+  => range a : a : s :-> Integer : s+inRangeCmp_+  = fromRange_+  # L.unpair+  # L.dupN @3+  # inRangeComp_ @lower+  # flip if_ (L.dropN @2 # push (-1))+      (inRangeComp_ @upper+      # if_ (push 0) (push 1)+      )++{- | Assert a value is in range; throw errors if it's under- or overflowing. See+'OnRangeAssertFailure' for information on how to specify errors.++If you don't need verbose error reporting, consider using+'assertInRangeSimple_'.+-}+assertInRange_+  :: forall range lower upper a s. (NiceRange range lower upper a, NiceComparable a, Dupable a)+  => OnRangeAssertFailure a+  -> range a : a : s :-> s+assertInRange_ OnRangeAssertFailure{..}+  = dip (toNamed #value)+  # L.dupN @2+  # L.dupN @2+  # fromRange_+  # L.unpair+  # L.dupN @3+  # fromNamed #value+  # inRangeComp_ @lower+  # if_+      ( dip (fromNamed #value)+      # inRangeComp_ @upper+      # if_ (L.dropN @2)+          ( rangeUpper_ # toNamed #boundary+          # push (isInclusive @upper)+          # mkRangeFailureInfo+          # orafOverflow+          )+      )+      ( L.dropN @2+      # rangeLower_ # toNamed #boundary+      # push (isInclusive @lower)+      # mkRangeFailureInfo+      # orafUnderflow+      )++{- | A cheaper version of 'assertInRange_' without fancy error reporting.++>>> let range = mkRange 123 456 :: Range Integer+>>> let err = failUsing [mt|failure|]+>>> assertInRangeSimple_ err -$ range ::: 123 ::: ()+()+>>> assertInRangeSimple_ err -$ range ::: 1 ::: ()+*** Exception: Reached FAILWITH instruction with '"failure"' ...+...+-}+assertInRangeSimple_+  :: forall range lower upper a s. (NiceRange range lower upper a, NiceComparable a, Dupable a)+  => (forall s' any. s' :-> any)+  -> range a : a : s :-> s+assertInRangeSimple_ err+  = inRange_ # if_ nop err++-- | Small helper to construct 'RangeFailureInfo' in Lorentz code.+mkRangeFailureInfo+  :: forall a s. IsoValue a+  => Bool : "boundary" :! a : "value" :! a : s :-> RangeFailureInfo a : s+mkRangeFailureInfo = coerce $ constructStack @(RangeFailureInfo a) @_ @s++-- | Information about the range check failure+data RangeFailureInfo a = RangeFailureInfo+  { rfiInclusive :: Bool -- ^ Whether the boundary is inclusive or not+  , rfiBoundary :: a     -- ^ The boundary that failed to check+  , rfiValue :: a        -- ^ The value that failed to check+  } deriving stock Generic+    deriving anyclass IsoValue+    deriving TypeHasFieldNamingStrategy via T.FieldCamelCase++instance TypeHasDoc a => TypeHasDoc (RangeFailureInfo a) where+  type TypeDocFieldDescriptions (RangeFailureInfo a) =+    '[ '( "RangeFailureInfo", '( 'Nothing,+            '[ '("rfiInclusive", "Whether the boundary is inclusive or not")+            ,  '("rfiBoundary", "The boundary that failed to check")+            ,  '("rfiValue", "The value that failed to check")+            ])+        )+     ]+  typeDocMdDescription = "Information about the range check failure"+  typeDocMdReference = poly1TypeDocMdReference+  typeDocHaskellRep+    = T.haskellRepMap (typeFieldNamingStrategy @(RangeFailureInfo a))+    $ concreteTypeDocHaskellRep @(RangeFailureInfo Integer)+  typeDocMichelsonRep = concreteTypeDocMichelsonRep @(RangeFailureInfo Integer)++{- | Specify on how to fail on range overflow or underflow in 'assertInRange_'.++This expects two always-failing Lorentz functions, one will be called on+underflow, another on overflow.++See 'customErrorORAF' for a simpler helper to use with custom Lorentz errors.++See 'mkOnRangeAssertFailureSimple' if you don't care about distinguishing+underflow and overflow.+-}+data OnRangeAssertFailure a = OnRangeAssertFailure+  { orafUnderflow :: forall s any. RangeFailureInfo a : s :-> any+  , orafOverflow :: forall s any. RangeFailureInfo a : s :-> any+  }++-- | Construct 'OnRangeAssertFailure' that performs the same action on both+-- overflow and underflow+mkOnRangeAssertFailureSimple+  :: (forall s any. RangeFailureInfo a : s :-> any)+  -> OnRangeAssertFailure a+mkOnRangeAssertFailureSimple f = OnRangeAssertFailure f f++{- | Construct 'OnRangeAssertFailure' that throws custom errors. Errors used+must be defined somewhere using @errorDocArg@ or equivalent, and must accept+'RangeFailureInfo' as the argument.++>>> :{+type RFII = RangeFailureInfo Integer+--+[errorDocArg| "overflowInt" exception "Integer range overflow" RFII |]+[errorDocArg| "underflowInt" exception "Integer range underflow" RFII |]+--+testCode :: Range Integer : Integer : s :-> s+testCode = assertInRange_ $ customErrorORAF ! #underflow #underflowInt ! #overflow #overflowInt+:}++>>> testCode -$ mkRange 0 10 ::: 123 ::: ()+*** Exception: Reached FAILWITH instruction with 'Pair "OverflowInt" (Pair True (Pair 10 123))'+...+>>> testCode -$ mkRange 0 10 ::: -1 ::: ()+*** Exception: Reached FAILWITH instruction with 'Pair "UnderflowInt" (Pair True (Pair 0 -1))'+...+>>> testCode -$ mkRange 0 10 ::: 5 ::: ()+()++-}+customErrorORAF+  :: (NiceConstant a+    , ErrorArg underflow ~ RangeFailureInfo a+    , ErrorArg overflow ~ RangeFailureInfo a+    , CustomErrorHasDoc underflow+    , CustomErrorHasDoc overflow+    )+  => ("underflow" :! Label underflow)+  -> ("overflow" :! Label overflow)+  -> OnRangeAssertFailure a+customErrorORAF (arg #underflow -> ufl) (arg #overflow -> ofl) = OnRangeAssertFailure+  { orafUnderflow = failCustom ufl+  , orafOverflow = failCustom ofl+  }++{- | A version of 'customErrorORAF' that uses the same error for both underflow+and overflow.++>>> :{+type RFII = RangeFailureInfo Integer+[errorDocArg| "rangeError" exception "Integer range check error" RFII |]+--+testCode :: Range Integer : Integer : s :-> s+testCode = assertInRange_ $ customErrorORAFSimple #rangeError+:}++>>> testCode -$ mkRange 0 10 ::: 123 ::: ()+*** Exception: Reached FAILWITH instruction with 'Pair "RangeError" (Pair True (Pair 10 123))'+...+>>> testCode -$ mkRange 0 10 ::: -1 ::: ()+*** Exception: Reached FAILWITH instruction with 'Pair "RangeError" (Pair True (Pair 0 -1))' ...+...+>>> testCode -$ mkRange 0 10 ::: 5 ::: ()+()+-}+customErrorORAFSimple+  :: (NiceConstant a+    , ErrorArg err ~ RangeFailureInfo a+    , CustomErrorHasDoc err+    )+  => Label err+  -> OnRangeAssertFailure a+customErrorORAFSimple err = mkOnRangeAssertFailureSimple $ failCustom err++{- | Check if a range is empty by construction. Note, however, this function is+not suitable for checking whether a range with both boundaries excluded is+empty, and hence will produce a type error when such a check is attempted.++>>> isRangeEmpty $ mkRange 0 1+False+>>> isRangeEmpty $ mkRange 0 0+False+>>> isRangeEmpty $ mkRange 0 -1+True++>>> isRangeEmpty $ mkRange @RangeIE 0 1+False+>>> isRangeEmpty $ mkRange @RangeIE 0 0+True+>>> isRangeEmpty $ mkRange @RangeIE 0 -1+True++>>> isRangeEmpty $ mkRange @RangeEI 0 1+False+>>> isRangeEmpty $ mkRange @RangeEI 0 0+True+>>> isRangeEmpty $ mkRange @RangeEI 0 -1+True++>>> isRangeEmpty $ mkRange @RangeEE 0 1+...+... error:+... This function can't be used to check whenter a range with both boundaries excluded is empty+...+-}+isRangeEmpty+  :: forall range lower upper a.+    (NiceRange range lower upper a, Ord a, CanCheckEmpty lower upper)+  => range a -> Bool+isRangeEmpty r = checkEmpty @lower @upper (rangeLower r) (rangeUpper r)++{- | Lorentz version of 'isRangeEmpty'. Same caveats apply.++>>> mkRange_ # isRangeEmpty_ -$ (#min :! 0) ::: (#max :! 1)+False+>>> mkRange_ # isRangeEmpty_ -$ (#min :! 0) ::: (#max :! 0)+False+>>> mkRange_ # isRangeEmpty_ -$ (#min :! 0) ::: (#max :! -1)+True++>>> mkRange_ @RangeIE # isRangeEmpty_ -$ (#min :! 0) ::: (#max :! 1)+False+>>> mkRange_ @RangeIE # isRangeEmpty_ -$ (#min :! 0) ::: (#max :! 0)+True+>>> mkRange_ @RangeIE # isRangeEmpty_ -$ (#min :! 0) ::: (#max :! -1)+True++>>> mkRange_ @RangeEI # isRangeEmpty_ -$ (#min :! 0) ::: (#max :! 1)+False+>>> mkRange_ @RangeEI # isRangeEmpty_ -$ (#min :! 0) ::: (#max :! 0)+True+>>> mkRange_ @RangeEI # isRangeEmpty_ -$ (#min :! 0) ::: (#max :! -1)+True++>>> mkRange_ @RangeEE # isRangeEmpty_ -$ (#min :! 0) ::: (#max :! 1)+...+... error:+... This function can't be used to check whenter a range with both boundaries excluded is empty+...+-}+isRangeEmpty_+  :: forall range lower upper a s.+    ( NiceRange range lower upper a+    , CanCheckEmpty lower upper+    , NiceComparable a+    )+  => range a : s :-> Bool : s+isRangeEmpty_+  = fromRange_+  # L.unpair+  # checkEmpty_ @lower @upper++-- | Helper class to check if a range is empty. Gives up when both boundaries+-- are exclusive.+class CanCheckEmpty l u where+  -- | Returns 'True' when a range is empty, 'False' otherwise.+  checkEmpty :: Ord a => a -> a -> Bool+  -- | Lorentz version of 'checkEmpty'.+  checkEmpty_ :: NiceComparable a => a : a : s :-> Bool : s+instance CanCheckEmpty 'IncludeBoundary 'IncludeBoundary where+  checkEmpty = (>)+  checkEmpty_ = L.gt+instance CanCheckEmpty 'IncludeBoundary 'ExcludeBoundary where+  checkEmpty = (>=)+  checkEmpty_ = L.ge+instance CanCheckEmpty 'ExcludeBoundary 'IncludeBoundary where+  checkEmpty = (>=)+  checkEmpty_ = L.ge+instance (Bottom, TypeError+  ('Text "This function can't be used to check whenter a \+    \range with both boundaries excluded is empty")) =>+  CanCheckEmpty 'ExcludeBoundary 'ExcludeBoundary where+  checkEmpty = no+  checkEmpty_ = no++{- | Assert a range is non-empty, run a failing function passed as the first+argument if it is. Note, however, this function is not suitable for checking+whether a range with both boundaries excluded is empty, and hence will produce a+type error when such a check is attempted.++>>> let err = push [mt|empty range|] # pair # failWith @(MText, Range Integer)+>>> pretty $ mkRange_ # assertRangeNonEmpty_ err -$ (#min :! 0) ::: (#max :! 1)+[0, 1]++>>> pretty $ mkRange_ # assertRangeNonEmpty_ err -$ (#min :! 0) ::: (#max :! 0)+[0, 0]++>>> pretty $ mkRange_ # assertRangeNonEmpty_ err -$ (#min :! 0) ::: (#max :! -1)+*** Exception: Reached FAILWITH instruction with 'Pair "empty range" (Pair 0 -1)' ...+...++>>> mkRange_ @RangeEE # assertRangeNonEmpty_ err -$ (#min :! 0) ::: (#max :! 1)+...+... error:+... This function can't be used to check whenter a range with both boundaries excluded is empty+...+-}+assertRangeNonEmpty_+  :: ( NiceRange range lower upper a+     , CanCheckEmpty lower upper+     , NiceComparable a+     , Dupable (range a)+     )+  => (forall s' any. range a : s' :-> any)+  -> range a : s :-> range a : s+assertRangeNonEmpty_ err = dup # isRangeEmpty_ # if_ err nop
src/Lorentz/StoreClass.hs view
@@ -17,6 +17,27 @@ We provide the most common building blocks for implementing these instances, see @Implementations@ section. +@OverloadedRecordDot@ is supported for field access, e.g. like this:++> stSetField this.sField1.ssField1.gField++(note `this` is mandatory)++However, there are some kinks. When using Lorentz with @RebindableSyntax@ and+@OverloadedRecordDot@, you'll want to hide @getField@ import from @Lorentz@ or+@Lorentz.ADT@, and instead import @GHC.Records@, otherwise GHC will try to use+@Lorentz.ADT.getField@ when desugaring record dots and produce inscrutable error+messages like this:++@+• Expected a type, but+  ‘"gField"’ has kind+  ‘ghc-prim-0.9.0:GHC.Types.Symbol’+@++If you need to use both @Lorentz.ADT.getField@ and record dots, consider+importing @Lorentz.ADT@ qualified.+ -} module Lorentz.StoreClass   ( -- * Preliminary@@ -109,6 +130,7 @@   import Data.Kind qualified as Kind+import GHC.Records qualified as GHC import GHC.TypeLits (KnownSymbol, Symbol)  import Lorentz.ADT@@ -163,6 +185,14 @@          IsLabel name (x p) where   fromLabel = FieldName +instance (KnownSymbol field, y ~ FieldRefObject (SelfRef :-| field), p ~ 'FieldRefTag)+  => GHC.HasField field (SelfRef p) (y p) where+  getField l = l :-| FieldName++instance (KnownSymbol field, y ~ FieldRefObject ((l :-| r) :-| field), p ~ 'FieldRefTag)+  => GHC.HasField field ((l :-| r) p) (y p) where+  getField l = l :-| FieldName+ instance KnownSymbol name => KnownFieldRef (name :: Symbol) where   type FieldRefObject name = FieldName name   mkFieldRef = FieldName@@ -1009,8 +1039,8 @@ -- -- Example: @stToField (#a :-| #b)@ fetches field @b@ in the type under field @a@. ----- If not favouring this name much, you can try an alias from--- "Lorentz.StoreClass.Extra".+-- If this syntax seems overly verbose, you can try using @OverloadedRecordDot@.+-- See module documentation for comments on that. infixr 8 :-| data (:-|) (l :: k1) (r :: k2) (p :: FieldRefTag) =   FieldRef l :-| FieldRef r
src/Lorentz/StoreClass/Extra.hs view
@@ -6,6 +6,7 @@ -- This is not part of the umbrella "Lorentz" module, you have to import -- this specially. module Lorentz.StoreClass.Extra+  {-# DEPRECATED "Use OverloadedRecordDot instead" #-}   ( (.)   ) where 
src/Lorentz/UParam.hs view
@@ -65,6 +65,7 @@ import Morley.AsRPC (HasRPCRepr(..)) import Morley.Michelson.Text import Morley.Michelson.Typed+import Morley.Util.Generic import Morley.Util.Label (Label) import Morley.Util.Markdown import Morley.Util.Type@@ -333,21 +334,38 @@       they come from constructor names. -} --- | Make up 'UParam' from ADT sum.------ Entry points template will consist of--- @(constructorName, constructorFieldType)@ pairs.--- Each constructor is expected to have exactly one field.+{- | Make up 'UParam' from ADT sum.++Entry points template will consist of+@(constructorName, constructorFieldType)@ pairs.+Each constructor is expected to have exactly one field.++Shows human-readable errors when @up@ is stuck.++>>> data Foo = Foo ()+>>> uparamFromAdt $ Foo ()+...+... GHC.Generics.Rep Foo+... is stuck. Likely+... Generic Foo+... instance is missing or out of scope.+...++>>> data Foo = Foo () deriving Generic+>>> uparamFromAdt $ Foo ()+UnsafeUParam (UnsafeMText {unMText = "Foo"},"\ENQ\ETX\v")++-} uparamFromAdt   :: UParamLinearize up   => up -> UParam (UParamLinearized up) uparamFromAdt = adtToRec . G.from  -- | Constraint required by 'uparamFromAdt'.-type UParamLinearize p = (Generic p, GUParamLinearize (G.Rep p))+type UParamLinearize p = (NiceGeneric p, GUParamLinearize (GRep p))  -- | Entry points template derived from given ADT sum.-type UParamLinearized p = GUParamLinearized (G.Rep p)+type UParamLinearized p = GUParamLinearized (GRep p)  -- | Generic traversal for conversion between ADT sum and 'UParam'. class GUParamLinearize (x :: Type -> Type) where
src/Lorentz/Wrappable.hs view
@@ -14,10 +14,35 @@ import Morley.Michelson.Typed (ToT) import Morley.Util.Named --- | Declares that this type is just a wrapper over some other type--- and it can be safely unwrapped to that inner type.+{- | Declares that this type is just a wrapper over some other type and it can+be safely unwrapped to that inner type.++Inspired by lens @Wrapped@.++Generic-derivable for newtypes.++>>> import Lorentz+>>> :{+newtype Foo = Foo Integer+  deriving stock Generic+  deriving anyclass (IsoValue, Unwrappable)+:}++@IsoValue@ instance is mandatory, but 'Generic' isn't:++>>> import Morley.Michelson.Typed.T+>>> :{+data Foo = Foo Integer ----- Inspired by lens @Wrapped@.+instance IsoValue Foo where+  type ToT Foo = TInt+  toVal (Foo i) = toVal i+  fromVal i = Foo $ fromVal i+--+instance Unwrappable Foo where+  type Unwrappabled Foo = Integer+:}+-} class ToT s ~ ToT (Unwrappabled s) => Unwrappable (s :: Type) where   -- | The type we unwrap to (inner type of the newtype).   --