Juvix interop: standard library

An outline of things that should be in the standard library:

Data structures

  • Tuples. These are somewhat “free” from cells, upon choosing an associativity direction (right) for cons.
  • Unit/Option/Maybe/\forall T. T + 1/whatever you call it
  • List. This is also somewhat “free” from cells, upon choosing a terminator (0).
  • Lazy streams. (Very slightly) involved to set up, easy to use.
  • Sets (balanced as trees)
  • Maps (balanced as above, but by key)

Arithmetics

  • Unsigned, natural-number arithmetic.
    • Both crash-on-underflow and unit-valued subtract.
    • The least surprising thing with regard to division (and have rem, and probably divrem giving a pair)
    • Both crash-on-underflow and unit-valued division (by zero)
  • Signed, integer arithmetic
    • As above, but subtract-underflow is no longer an issue.
  • Probably not floating-point.
    • Wildly complex out of proportion to its usefulness. Worth keeping in mind as a far-future feature (we might want to perform some light numerical calculus operations)
  • Decimals and Rationals
    • Wildly useful out of proportion to its complexity; worth having now.
    • by “decimals” I mean fixed-point decimals under the usual simple precision rules
    • Rationals: p/q, p integers
      • positive rationals over naturals? maybe
  • Timestamps
    • Absolute and Relative time; this is so you can e.g. add “one hour” to “now”.
    • This is actually just a format/pretty-printing convenience over naturals. (We can easily choose time 0 to be older than the universe; e.g. Hoon’s time 0 is midnight, January 1, 292,277,024,401 BC, chosen to place January 1, 2000 in about the middle of the 64-bit time range.)
    • Pick exactly one calendar (again, this is just pretty-printing.)
  • Binaries, bit and byte fiddling, and so on.
    • This is only vaguely arithmetic, but includes things that feel arithmetic like bitwise xor.

Library Functions

  • Support for all the data structures mentioned above.
    • Unit: monadic apply, need, just, &c.
    • List: map, filter, &c.
    • Lazy stream: as above.
    • Set: insert, member?, union, intersect, delete, difference, &c.
    • Map: as above, but values add complexity over just keys
      • e.g., map union needs to decide what to do with duplicate keys (so map union takes 2 maps and also a function to decide what to do with duplicate keys).
  • Cryptography primitives
    • Cryptographic hashes
      • SHA2: generic cryptography hash. useful everywhere
      • others? poseidon?
    • Ed25519 for signatures, DH
    • AES? (as a generic block cipher)
      • block cipher modes over it? GCM?
    • As I understand it, Poseidon somehow works for encryption as well
      • All I know is that someone mentioned it and the Poseidon website has a page on it
    • KDFs, perhaps?
      • argon, e.g.; deliberately-slow hashes
    • Simple constructions over these? (e.g. hmac)
    • Generic interfaces
      • for e.g. “signer”, “hasher”
    • PRNGs fit in here as well.
  • Low-level Nock support infrastructure.
    • Mug (fast hash-table style hash, used for equality)
      • also includes set/map balancing hashes
    • Jam, cue
    • Type serialization?
      • This depends on future work to know what they look like.
    • Debugging affordances (to the extent they’re in stdlib and not keyword magic)
    • Reflection, e.g., self-virtualization
      • It’s easy to write Nock in Nock and proceed to jet it with the original Nock interpreter itself.
  • Binary processing
    • This means e.g. strings, and other binary data.
    • Can we compile elixir binary patterns to this in our macros? I believe yes.
  • Core conveniences
    • Higher-order functions
      • curry, compose, &c.
    • Core reflection
      • may require runtime types

@paulcadman and others, place wishlist items below this line


Thanks for the write-up. Just one note to keep in mind here is that we’ll want to carefully reason about / separate which primitives and library functions are supported by the different backends, where applicable, and perhaps occasionally have abstract functions like “cryptographic_hash” which may be compiled differently for different backends (but thereby allow the same resource logic Juvix code to be used). I’m sure you’ve thought about this problem already and may have a more sophisticated plan, but just wanted to mention it.

Should we pick quantum secure algorithms for, e.g., signatures?
There are predictions that quantum computers could become an issue for Bitcoin/Ethereum etc. in ~2–5 years from now.

1 Like

Do we want that at the library level?

As you mention, this seems to be a question about being compiled differently for different backends. So shouldn’t this choice be at the level of the Juvix compiler if we assume we are using Juvix as a front-end here?

1 Like

At some point, yes. I don’t think it’s the most urgent thing right now, but before we launch “real value Anoma”, we’ll want to consider this question (imo).

Hopefully, adding new primitives to a backend (and potentially supporting those primitives in the standard library) doesn’t require modification of the Juvix compiler itself, but the Juvix compiler would need to be aware of some standard format for declaring primitives, the calling conventions of each backend, etc. (I am not familiar with the exact details of how Juvix handles backend-specific functions at the moment). Does that answer your question?

1 Like

Hm, I may have just misunderstood what you mean here, but I don’t quite get the idea yet. Let me try to elaborate my question below.

The statement

from your original post suggests to me that the function cryptographic_hash is instantiated at a level be compiled further, i.e. there is a compiler involved. Yet here we are discussing the standard library, i.e. Nockma standard library, i.e. Nockma code which will be in the sample if whatever other Nockma expression we evaluate. So there isn’t anywhere further to compile down, if I understand everything correctly. The standard library is just there. So the described function seems to be needed to be provided at a Juvix level.

Or maybe “compiled” here is used in the meaning of the fact that we have a wrapper gate cha which accepts some argument treated as a backend-designator and then cases on? Depending on the argument, a different hash function is provided. However then the argument should be provided at a Juvix->Nockma compilation level.

Did I understand the point correctly? Hopefully I did not misinterpret anything.

1 Like

Ah, I see - to be clear, I am not proposing any additional case-switching or functionality at the Nockma standard library level. Rather, I’m proposing that we may have a “high-level” standard library of abstract functions (such as cryptographic_hash) which are automatically compiled to different functions for different backend standard libraries (but where we need to write the functions in a way such that this will work, e.g. ensure that certain properties are preserved).

Mostly my ask is simply to keep in mind that there are multiple backends, and that as much as possible we want code written in Juvix to work across them. I’m not asking for any specific changes to this proposal.

1 Like