@@ -36,7 +36,8 @@ module PlutusCore.Constant.Typed
3636 , HasConstantIn
3737 , KnownTypeAst (.. )
3838 , KnownType (.. )
39- , makeKnownNoEmit
39+ , readKnownSelf
40+ , makeKnownOrFail
4041 , SomeConstant (.. )
4142 , SomeConstantOf (.. )
4243 ) where
@@ -408,24 +409,24 @@ newtype Opaque term (rep :: GHC.Type) = Opaque
408409
409410-- | Throw an 'UnliftingError' saying that the received argument is not a constant.
410411throwNotAConstant
411- :: (MonadError (ErrorWithCause err term ) m , AsUnliftingError err )
412- => term -> m r
413- throwNotAConstant = throwingWithCause _UnliftingError " Not a constant" . Just
412+ :: (MonadError (ErrorWithCause err cause ) m , AsUnliftingError err )
413+ => Maybe cause -> m r
414+ throwNotAConstant = throwingWithCause _UnliftingError " Not a constant"
414415
415416class AsConstant term where
416417 -- | Unlift from the 'Constant' constructor throwing an 'UnliftingError' if the provided @term@
417418 -- is not a 'Constant'.
418419 asConstant
419- :: (MonadError (ErrorWithCause err term ) m , AsUnliftingError err )
420- => term -> m (Some (ValueOf (UniOf term )))
420+ :: (MonadError (ErrorWithCause err cause ) m , AsUnliftingError err )
421+ => Maybe cause -> term -> m (Some (ValueOf (UniOf term )))
421422
422423class FromConstant term where
423424 -- | Wrap a Haskell value as a @term@.
424425 fromConstant :: Some (ValueOf (UniOf term )) -> term
425426
426427instance AsConstant (Term TyName Name uni fun ann ) where
427- asConstant (Constant _ val) = pure val
428- asConstant term = throwNotAConstant term
428+ asConstant _ (Constant _ val) = pure val
429+ asConstant mayCause _ = throwNotAConstant mayCause
429430
430431instance FromConstant (Term tyname name uni fun () ) where
431432 fromConstant = Constant ()
@@ -461,35 +462,41 @@ latter breaks on @m a@
461462
462463-- See Note [KnownType's defaults].
463464-- | Haskell types known to exist on the PLC side.
465+ -- Both the methods take a @Maybe cause@ argument to report the cause of a potential failure.
466+ -- @cause@ is different to @term@ to support evaluators that distinguish between terms and values
467+ -- (@makeKnown@ normally constructs a value, but it's convenient to report the cause of a failure
468+ -- as a term). Note that an evaluator might require the cause to be computed lazily for best
469+ -- performance on the happy path and @Maybe@ ensures that even if we somehow force the argument,
470+ -- the cause stored in it is not forced due to @Maybe@ being a lazy data type.
464471class KnownTypeAst (UniOf term ) a => KnownType term a where
465472 -- | Convert a Haskell value to the corresponding PLC term.
466473 -- The inverse of 'readKnown'.
467474 makeKnown
468- :: ( MonadEmitter m , MonadError err m , AsEvaluationFailure err
475+ :: ( MonadEmitter m , MonadError ( ErrorWithCause err cause ) m , AsEvaluationFailure err
469476 )
470- => a -> m term
477+ => Maybe cause -> a -> m term
471478 default makeKnown
472- :: ( MonadError err m
479+ :: ( MonadError ( ErrorWithCause err cause ) m
473480 , KnownBuiltinType term a
474481 )
475- => a -> m term
482+ => Maybe cause -> a -> m term
476483 -- Forcing the value to avoid space leaks. Note that the value is only forced to WHNF,
477484 -- so care must be taken to ensure that every value of a type from the universe gets forced
478485 -- to NF whenever it's forced to WHNF.
479- makeKnown x = pure . fromConstant . someValue $! x
486+ makeKnown _ x = pure . fromConstant . someValue $! x
480487
481488 -- | Convert a PLC term to the corresponding Haskell value.
482489 -- The inverse of 'makeKnown'.
483490 readKnown
484- :: ( MonadError (ErrorWithCause err term ) m , AsUnliftingError err , AsEvaluationFailure err
491+ :: ( MonadError (ErrorWithCause err cause ) m , AsUnliftingError err , AsEvaluationFailure err
485492 )
486- => term -> m a
493+ => Maybe cause -> term -> m a
487494 default readKnown
488- :: ( MonadError (ErrorWithCause err term ) m , AsUnliftingError err
495+ :: ( MonadError (ErrorWithCause err cause ) m , AsUnliftingError err
489496 , KnownBuiltinType term a
490497 )
491- => term -> m a
492- readKnown term = asConstant term >>= \ case
498+ => Maybe cause -> term -> m a
499+ readKnown mayCause term = asConstant mayCause term >>= \ case
493500 Some (ValueOf uniAct x) -> do
494501 let uniExp = knownUni @ _ @ (UniOf term ) @ a
495502 case uniAct `geq` uniExp of
@@ -500,34 +507,57 @@ class KnownTypeAst (UniOf term) a => KnownType term a where
500507 , " expected: " ++ gshow uniExp
501508 , " ; actual: " ++ gshow uniAct
502509 ]
503- throwingWithCause _UnliftingError err $ Just term
504-
505- makeKnownNoEmit :: (KnownType term a , MonadError err m , AsEvaluationFailure err ) => a -> m term
506- makeKnownNoEmit = unNoEmitterT . makeKnown
510+ throwingWithCause _UnliftingError err mayCause
511+
512+ -- | Same as 'readKnown', but the cause of a potential failure is the provided term itself.
513+ readKnownSelf
514+ :: ( KnownType term a
515+ , MonadError (ErrorWithCause err term ) m , AsUnliftingError err , AsEvaluationFailure err
516+ )
517+ => term -> m a
518+ readKnownSelf term = readKnown (Just term) term
519+
520+ -- | A transformer for fitting a monad not carrying the cause of a failure into 'makeKnown'.
521+ newtype NoCauseT (term :: GHC. Type ) m a = NoCauseT
522+ { unNoCauseT :: m a
523+ } deriving newtype (Functor , Applicative , Monad )
524+
525+ instance (MonadError err m , AsEvaluationFailure err ) =>
526+ MonadError (ErrorWithCause err term ) (NoCauseT term m ) where
527+ throwError _ = NoCauseT $ throwError evaluationFailure
528+ NoCauseT a `catchError` h =
529+ NoCauseT $ a `catchError` \ err ->
530+ unNoCauseT . h $ ErrorWithCause err Nothing
531+
532+ -- | Same as 'makeKnown', but allows for neither emitting nor storing the cause of a failure.
533+ -- For example the monad can be simply 'EvaluationResult'.
534+ makeKnownOrFail :: (KnownType term a , MonadError err m , AsEvaluationFailure err ) => a -> m term
535+ makeKnownOrFail = unNoCauseT . unNoEmitterT . makeKnown Nothing
507536
508537instance KnownTypeAst uni a => KnownTypeAst uni (EvaluationResult a ) where
509538 toTypeAst _ = toTypeAst $ Proxy @ a
510539
511540instance (KnownTypeAst (UniOf term ) a , KnownType term a ) =>
512541 KnownType term (EvaluationResult a ) where
513- makeKnown EvaluationFailure = throwError evaluationFailure
514- makeKnown (EvaluationSuccess x) = makeKnown x
542+ makeKnown mayCause EvaluationFailure = throwingWithCause _EvaluationFailure () mayCause
543+ makeKnown mayCause (EvaluationSuccess x) = makeKnown mayCause x
515544
516545 -- Catching 'EvaluationFailure' here would allow *not* to short-circuit when 'readKnown' fails
517546 -- to read a Haskell value of type @a@. Instead, in the denotation of the builtin function
518547 -- the programmer would be given an explicit 'EvaluationResult' value to handle, which means
519548 -- that when this value is 'EvaluationFailure', a PLC 'Error' was caught.
520549 -- I.e. it would essentially allow us to catch errors and handle them in a programmable way.
521550 -- We forbid this, because it complicates code and isn't supported by evaluation engines anyway.
522- readKnown = throwingWithCause _UnliftingError " Error catching is not supported" . Just
551+ readKnown mayCause _ =
552+ throwingWithCause _UnliftingError " Error catching is not supported" mayCause
523553
524554instance KnownTypeAst uni a => KnownTypeAst uni (Emitter a ) where
525555 toTypeAst _ = toTypeAst $ Proxy @ a
526556
527557instance KnownType term a => KnownType term (Emitter a ) where
528- makeKnown = unEmitter >=> makeKnown
558+ makeKnown mayCause = unEmitter >=> makeKnown mayCause
529559 -- TODO: we really should tear 'KnownType' apart into two separate type classes.
530- readKnown = throwingWithCause _UnliftingError " Can't unlift an 'Emitter'" . Just
560+ readKnown mayCause _ = throwingWithCause _UnliftingError " Can't unlift an 'Emitter'" mayCause
531561
532562-- | For unlifting from the 'Constant' constructor. For cases where we care about having a type tag
533563-- in the denotation of a builtin rather than full unlifting to a specific built-in type.
@@ -543,8 +573,8 @@ instance (uni ~ uni', KnownTypeAst uni rep) => KnownTypeAst uni (SomeConstant un
543573
544574instance (HasConstantIn uni term , KnownTypeAst uni rep ) =>
545575 KnownType term (SomeConstant uni rep ) where
546- makeKnown = pure . fromConstant . unSomeConstant
547- readKnown = fmap SomeConstant . asConstant
576+ makeKnown _ = pure . fromConstant . unSomeConstant
577+ readKnown mayCause = fmap SomeConstant . asConstant mayCause
548578
549579{- | 'SomeConstantOf' is similar to 'SomeConstant': while the latter is for unlifting any
550580constants, the former is for unlifting constants of a specific polymorphic built-in type
@@ -606,18 +636,18 @@ data ReadSomeConstantOf m uni f reps =
606636
607637instance (KnownBuiltinTypeIn uni term f , All (KnownTypeAst uni ) reps , HasUniApply uni ) =>
608638 KnownType term (SomeConstantOf uni f reps ) where
609- makeKnown = pure . fromConstant . runSomeConstantOf
639+ makeKnown _ = pure . fromConstant . runSomeConstantOf
610640
611- readKnown term = asConstant term >>= \ case
641+ readKnown (mayCause :: Maybe cause ) term = asConstant mayCause term >>= \ case
612642 Some (ValueOf uni xs) -> do
613643 let uniF = knownUni @ _ @ _ @ f
614644 err = fromString $ concat
615645 [ " Type mismatch: "
616646 , " expected an application of: " ++ gshow uniF
617647 , " ; but got the following type: " ++ gshow uni
618648 ]
619- wrongType :: (MonadError (ErrorWithCause err term ) m , AsUnliftingError err ) => m a
620- wrongType = throwingWithCause _UnliftingError err $ Just term
649+ wrongType :: (MonadError (ErrorWithCause err cause ) m , AsUnliftingError err ) => m a
650+ wrongType = throwingWithCause _UnliftingError err mayCause
621651 -- In order to prove that the type of @xs@ is an application of @f@ we need to
622652 -- peel all type applications off in the type of @xs@ until we get to the head and then
623653 -- check that the head is indeed @f@. Each peeled type application becomes a
@@ -663,12 +693,12 @@ instance KnownTypeAst uni rep => KnownTypeAst uni (Opaque term rep) where
663693 toTypeAst _ = toTypeAst $ Proxy @ rep
664694
665695instance (term ~ term' , KnownTypeAst (UniOf term ) rep ) => KnownType term (Opaque term' rep ) where
666- makeKnown = pure . unOpaque
667- readKnown = pure . Opaque
696+ makeKnown _ = pure . unOpaque
697+ readKnown _ = pure . Opaque
668698
669699instance uni `Contains ` Integer => KnownTypeAst uni Integer
670700instance uni `Contains ` BS. ByteString => KnownTypeAst uni BS. ByteString
671- instance uni `Contains ` Text. Text => KnownTypeAst uni Text. Text
701+ instance uni `Contains ` Text. Text => KnownTypeAst uni Text. Text
672702instance uni `Contains ` () => KnownTypeAst uni ()
673703instance uni `Contains ` Bool => KnownTypeAst uni Bool
674704instance uni `Contains ` [a ] => KnownTypeAst uni [a ]
@@ -677,7 +707,7 @@ instance uni `Contains` Data => KnownTypeAst uni Data
677707
678708instance KnownBuiltinType term Integer => KnownType term Integer
679709instance KnownBuiltinType term BS. ByteString => KnownType term BS. ByteString
680- instance KnownBuiltinType term Text. Text => KnownType term Text. Text
710+ instance KnownBuiltinType term Text. Text => KnownType term Text. Text
681711instance KnownBuiltinType term () => KnownType term ()
682712instance KnownBuiltinType term Bool => KnownType term Bool
683713instance KnownBuiltinType term [a ] => KnownType term [a ]
@@ -697,11 +727,11 @@ instance uni `Includes` Integer => KnownTypeAst uni Int where
697727
698728-- See Note [Int as Integer].
699729instance KnownBuiltinType term Integer => KnownType term Int where
700- makeKnown = makeKnown . toInteger
701- readKnown term = do
702- i :: Integer <- readKnown term
730+ makeKnown mayCause = makeKnown mayCause . toInteger
731+ readKnown mayCause term = do
732+ i :: Integer <- readKnown mayCause term
703733 unless (fromIntegral (minBound :: Int ) <= i && i <= fromIntegral (maxBound :: Int )) $
704- throwingWithCause _EvaluationFailure () $ Just term
734+ throwingWithCause _EvaluationFailure () mayCause
705735 pure $ fromIntegral i
706736
707737-- Utils
0 commit comments