Skip to content

Conversation

@SeungheonOh
Copy link
Collaborator

@SeungheonOh SeungheonOh commented Nov 20, 2025

You can now use PlutusTx.Builtins.Opaque.mkNil @Bool and mkNil @BuiltinByteString.

This doesn't add support for arbitrarily nested array. Only adds support for Bool and ByteString.

I'm figuring out making this work with any subtype of default universe, so please don't merge yet

@SeungheonOh SeungheonOh self-assigned this Nov 20, 2025
@SeungheonOh SeungheonOh requested review from kwxm and zliu41 November 20, 2025 14:52
@SeungheonOh SeungheonOh changed the title Support mkNil for Bool and BuiltinByteString Support mkNil for any builtin types Nov 21, 2025
@SeungheonOh SeungheonOh added the No Changelog Required Add this to skip the Changelog Check label Nov 21, 2025
@SeungheonOh
Copy link
Collaborator Author

I made it so that now mkNil will also work on any types.

| (tyCon, tyArg1, tyArg2) == (builtinPairTyCon, builtinDataTyCon, builtinDataTyCon) ->
pure $ PLC.mkConstant annMayInline ([] @(PLC.Data, PLC.Data))
_ -> throwPlain $ CompilationError "'mkNil' applied to an unknown type"
| GHC.getName n == mkNilOpaqueName -> compileMkNil ty
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

even though we now match mkNil directly and have "OPAQUE" for mkNil, we still need mkNilOpaque because for some reason ghc would ignore OPAQUE and use mkNilOpaque anyways.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we have OPAQUE for mkNil?
image

IIRC the point of this design was to make sure that some type class indirection doesn't get in the way, but I don't remember if it was necessary.

-> m (PIRTerm uni fun)
compileMkNil ty =
let
tyToUniverse :: GHC.Type -> Maybe (SomeTypeIn' PLC.DefaultUni)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there a way to do this with existing SomeTypeIn? SomeTypeIn won't work because it has existential kind and it's impossible to force that to Type...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you can avoid existentials altogether and do something like

withCompiledMkNil :: GHC.Type -> (forall a. PLC.DefaultUni (Esc a) -> m (PIRTerm uni fun))  -> m (PIRTerm uni fun)

but I also think that your version is simpler, so who cares.

Copy link
Contributor

@effectfully effectfully left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're my hero.

This was my original idea on how to make it work in the general case:

The only more or less reliable way I could think of is serializing the type at the type level into a Symbol (to make sure it's fully done at compile time), requiring it to be KnownSymbol, then extracting the resulting String in the compiler and parsing it back to get a DefaultUni object. Sounds pretty crazy, but I can't think of another way of parsing a Haskell value back after embedding it into a CoreExpr.

But that's a lot of effort for something rather insignificant and your solution should be enough in most cases (I don't wanna think when it's not enough).

I'll check if I can it statically detectable that an instance is missing.

{- | Identical to `SomeTypeIn` but without existential kind.
-}
type SomeTypeIn' :: (Kind.Type -> Kind.Type) -> Kind.Type
data SomeTypeIn' uni = forall a. SomeTypeIn' !(uni (PLC.Esc a))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could call it SomeStarIn or something. Just nitpicking obviously.

-> m (PIRTerm uni fun)
compileMkNil ty =
let
tyToUniverse :: GHC.Type -> Maybe (SomeTypeIn' PLC.DefaultUni)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you can avoid existentials altogether and do something like

withCompiledMkNil :: GHC.Type -> (forall a. PLC.DefaultUni (Esc a) -> m (PIRTerm uni fun))  -> m (PIRTerm uni fun)

but I also think that your version is simpler, so who cares.

| (tyCon, tyArg1, tyArg2) == (builtinPairTyCon, builtinDataTyCon, builtinDataTyCon) ->
pure $ PLC.mkConstant annMayInline ([] @(PLC.Data, PLC.Data))
_ -> throwPlain $ CompilationError "'mkNil' applied to an unknown type"
| GHC.getName n == mkNilOpaqueName -> compileMkNil ty
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we have OPAQUE for mkNil?
image

IIRC the point of this design was to make sure that some type class indirection doesn't get in the way, but I don't remember if it was necessary.

import Data.Map qualified as Map
import Data.Text qualified as T

{- | Identical to `SomeTypeIn` but without existential kind.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Elaborate why the existential kind is problematic?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

No Changelog Required Add this to skip the Changelog Check

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants