Commit 76bd5090 authored by Eric Myhre's avatar Eric Myhre

Readme and key Node interface docs improvements.

The readme hadn't seen an update for quite a long time, so it was just
overdue for it.  I think the comments about where to expect changes
at this point are practically accurate and now are also stated.

The Node interface, despite being so central to the entire system,
was missing a doc block on the type as a whole!  It now has one.
(I'd like to continue to iterate on this and have it do more linking
out to ipld docs for some of the more conceptual parts, but we've
got a lot of work to do on that content as well, so, for now just
doing it here is better than not having it.)
Signed-off-by: default avatarEric Myhre <hash@exultant.us>
parent 8735bb15
go-ipld-prime
=============
`go-ipld-prime` is an implementation of the IPLD spec interfaces, a default "native" implementation of IPLD based on CBOR, and tooling for basic operations on IPLD objects.
`go-ipld-prime` is an implementation of the IPLD spec interfaces,
a batteries-included codec implementations of IPLD for CBOR and JSON,
and tooling for basic operations on IPLD objects (traversals, etc).
API
---
- `github.com/ipld/go-ipld-prime` -- imported as just `ipld` -- contains interfaces for IPLD objects. You can implement these interfaces too. This package also provides a concrete implementation of merklepaths, and contains useful traversal functions.
- `github.com/ipld/go-ipld-prime/impl/cbor` -- imported as `ipldcbor` -- implements the IPLD interfaces backed with CBOR serialization. There are lots of handy methods for you to convert other Go types to and from these nodes.
- `github.com/ipld/go-ipld-prime/cmd/ipld` -- provides a standalone command-line tool for useful operations, such as processing objects and producing CIDs (hashes) for their canonicalized IPLD forms.
The API is split into several packages based on responsibly of the code.
The most central interfaces are the base package,
but you'll certainly need to import additional packages to get concrete implementations into action.
Roughly speaking, the core package interfaces are all about the IPLD Data Model;
the encoding packages contain some codecs, and turn serial data into Data Model;
the traversal package is an example of higher-order functions on the Data Model;
concrete 'Node' implementations ready to use can be found under 'impl/*';
and several additional packages contain advanced features such as IPLD Schemas.
(Because the codecs, as well as higher-order features like traversals, are
implemented in a separate package from the core interfaces or any of the Node implementations,
you can be sure they're not doing any funky "magic" -- all this stuff will work the same
if you want to write your own extensions, whether for new Node implementations
or new codecs, or new higher-order order functions!)
- `github.com/ipld/go-ipld-prime` -- imported as just `ipld` -- contains the core interfaces for IPLD. The most important interfaces are `Node`, `NodeBuilder`, `Path`, and `Link`.
- `github.com/ipld/go-ipld-prime/impl/free` -- imported as `ipldfree` -- provides concrete implementations of `Node` and `NodeBuilder` which work for any kind of data.
- `github.com/ipld/go-ipld-prime/impl/cbor` -- imported as `ipldcbor` -- provides concrete implementations of `Node` and `NodeBuilder` which have some special features to accelerate certain workloads with CBOR.
- `github.com/ipld/go-ipld-prime/traversal` -- contains higher-order functions for traversing graphs of data easily.
- `github.com/ipld/go-ipld-prime/traversal/selector` -- contains selectors, which are sort of like regexps, but for trees and graphs of IPLD data!
- `github.com/ipld/go-ipld-prime/encoding` -- parent package of all the codec implementations!
- `github.com/ipld/go-ipld-prime/encoding/dagcbor` -- implementations of marshalling and unmarshalling as CBOR (a fast, binary serialization format).
- `github.com/ipld/go-ipld-prime/encoding/dagjson` -- implementations of marshalling and unmarshalling as JSON (a popular human readable format).
- `github.com/ipld/go-ipld-prime/linking/cid` -- imported as `cidlink` -- provides concrete implementations of `Link` as a CID. Also, the multicodec registry.
- `github.com/ipld/go-ipld-prime/schema` -- contains the `schema.Type` declarations, which represent IPLD Schema type information.
- `github.com/ipld/go-ipld-prime/impl/typed` -- contains the `typed.Node` interface, which enhances the basic `Node` to have additional features described by IPLD Schemas.
Other IPLD Libraries
--------------------
The IPLD specifications are designed to be language-agnostic.
Many implementations exist in a variety of languages.
For overall behaviors and specifications, refer to the specs repo:
https://github.com/ipld/specs/
### distinctions from go-ipld-interface&go-ipld-cbor
This library ("go ipld prime") is the current head of development for golang IPLD,
but several other libraries exist which are widely deployed.
This library is a clean take on the IPLD interfaces and addresses several design decisions very differently than existing libraries:
- The Node interfaces are minimal;
- The Node interfaces are minimal (and match cleanly to the IPLD Data Model);
- Many features known to be legacy are dropped;
- The Link implementations are purely CIDs;
- The Path implementations are provided in the same box;
- The CBOR implementation is provided in the same box;
- The JSON and CBOR implementations are provided in the same box;
- And several odd dependencies on blockstore and other interfaces from the rest of the IPFS ecosystem are removed.
Most of these changes are on the roadmap for the existing IPLD projects as well, but a clean break v2 simply seemed like a clearer project-management path to getting to the end.
Many of these changes had been discussed for the other IPLD codebases as well,
but we chose clean break v2 as a more viable project-management path.
Both the existing IPLD libraries and go-ipld-prime can co-exist on the same import path, and refer to the same kinds of serial data.
Projects wishing to migrate can do so smoothly and at their leisure.
There is no explicit deprecation timeline for the earlier golang IPLD libraries,
but you should expect new features *here*, rather than in those libraries.
Change Policy
-------------
The go-ipld-prime library is already usable. We are also still in development, and may still change things.
Using a commit hash when depending on this library is advisable (as it is with any other).
We may sometimes tag releases, but it's just as acceptable to track commits on master without the indirection.
The following are all norms you can expect of changes to this codebase:
- The `master` branch will not be force-pushed.
- (exceptional circumstances may exist, but such exceptions will only be considered valid for about as long after push as the "$N-second-rule" about dropped food).
- Therefore, commit hashes on master are gold to link against.
- All other branches *will* be force-pushed.
- Therefore, commit hashes not reachable from the master branch are inadvisable to link against.
- If it's on master, it's understood to be good, in as much as we can tell.
- Development proceeds -- both starting from and ending on -- the `master` branch.
- There are no other long-running supported-but-not-master branches.
- The existence of tags at any particular commit do not indicate that we will consider starting a long running and supported diverged branch from that point, nor start doing backports, etc.
- All changes are presumed breaking until proven otherwise; and we don't have the time and attention budget at this point for doing the "proven otherwise".
- All consumers updating their libraries should run their own compiler, linking, and test suites before assuming the update applies cleanly -- as is good practice regardless.
- Any idea of semver indicating more or less breakage should be treated as a street vendor selling potions of levitation -- it's likely best disregarded.
None of this is to say we'll go breaking things willy-nilly for fun; but it *is* to say:
- Staying close to master is always better than not staying close to master;
- and trust your compiler and your tests rather than tea-leaf patterns in a tag string.
package ipld
// Node represents a value in IPLD. Any point in a tree of data is a node:
// scalar values (like int, string, etc) are nodes, and
// so are recursive values (like map and list).
//
// Nodes and kinds are described in the IPLD specs at
// https://github.com/ipld/specs/blob/master/data-model-layer/data-model.md .
//
// Methods on the Node interface cover the superset of all possible methods for
// all possible kinds -- but some methods only make sense for particular kinds,
// and thus will only make sense to call on values of the appropriate kind.
// (For example, 'Length' on an int doesn't make sense,
// and 'AsInt' on a map certainly doesn't work either!)
// Use the ReprKind method to find out the kind of value before
// calling kind-specific methods.
// Individual method documentation state which kinds the method is valid for.
// (If you're familiar with the stdlib reflect package, you'll find
// the design of the Node interface very comparable to 'reflect.Value'.)
//
// The Node interface is read-only. All of the methods on the interface are
// for examining values, and implementations should be immutable.
// The companion interface, NodeBuilder, provides the matching writable
// methods, and should be use to create a (thence immutable) Node.
//
// Keeping Node immutable and separating mutation into NodeBuilder makes
// it possible to perform caching (or rather, memoization, since there's no
// such thing as cache invalidation for immutable systems) of computed
// properties of Node; use copy-on-write algorithms for memory efficiency;
// and to generally build pleasant APIs.
// Many library functions will rely on the immutability of Node (e.g.,
// assuming that pointer-equal nodes do not change in value over time),
// so any user-defined Node implementations should be careful to uphold
// the immutability contract.)
//
// There are many different concrete types which implement Node.
// The primary purpose of various node implementations is to organize
// memory in the program in different ways -- some in-memory layouts may
// be more optimal for some programs than others, and changing the Node
// (and NodeBuilder) implementations lets the programmer choose.
//
// For concrete implementations of Node, check out the "./impl/" folder,
// and the packages within it.
// "impl/free" should probably be your first start; the Node and NodeBuilder
// implementations in that package work for any data.
// Other packages are optimized for specific use-cases.
// Codegen tools can also be used to produce concrete implementations of Node;
// these may be specific to certain data, but still conform to the Node
// interface for interoperability and to support higher-level functions.
//
// Nodes may also be *typed* -- see the 'schema' and 'impl/typed' packages.
// Typed nodes have additional constraints and behaviors (and have a
// `.Type().Kind()` in addition to their `.ReprKind()`!), but still behave
// as a regular Node in all the basic ways.
type Node interface {
// Kind returns a value from the ReprKind enum describing what the
// ReprKind returns a value from the ReprKind enum describing what the
// essential serializable kind of this node is (map, list, int, etc).
// Most other handling of a node requires first switching upon the kind.
ReprKind() ReprKind
......
......@@ -17,12 +17,27 @@ package ipld
// has a NodeBuilder implementation that produces new nodes of that same
// package's type.
//
// Most Node implementations also have a method which returns a NodeBuilder
// that produces more nodes of their same concrete implementation type.
// The Node interface includes a method which returns a NodeBuilder;
// this builder must be able to produce a new node of the same concrete
// implementation as the original node.
// This is useful for algorithms that work on trees of nodes: this NodeBuilder
// getter will be used when an update deep in the tree causes a need to
// create several new nodes to propagate the change up through parent nodes.
//
// NodeBuilder instances obtained from `Node.NodeBuilder()` may carry some
// additional logic or constraints with them to the new Node they produce.
// For example, a Node which is implemented using reflection to bind to a
// natively-typed struct will yield a NodeBuilder which contains a
// `reflect.Type` handle it can use to create a new value of that native type;
// similarly, schema-typed Nodes will yield a NodeBuilder that keeps the schema
// info and type constraints from that Node!
// (Continuing the typed.Node example: if you have a typed.Node that is
// constrained to be of some `type Foo = {Bar:Baz}` type, then any new Node
// produced from its NodeBuilder will still answer
// `n.(typed.Node).Type().Name()` as `Foo`; and if
// `n.NodeBuilder().AmendMap().Insert(...)` is called with nodes of unmatching
// type given to the insertion, the builder will error!)
//
// The NodeBuilder retrieved from a Node can also be used to do *updates*:
// consider the AmendMap and AmendList methods. These methods are useful
// not just for programmer convenience, but also because they can reuse memory,
......@@ -31,17 +46,6 @@ package ipld
// methods are equivalent to their Create* counterparts. As there's no
// "existing" node for them to refer to, it's treated the same as amending
// an empty node.)
//
// NodeBuilder instances obtained from Node.GetBuilder may carry some of the
// additional logic of their parent with them to the new Node they produce.
// For example, the NodeBuilder from typed.Node.GetBuilder may keep the type
// info and type constraints of their parent with them!
// (Continuing the typed.Node example: if you have a typed.Node that is
// constrained to be of some `type Foo = {Bar:Baz}` type, then any new Node
// produced from its NodeBuilder will still answer
// `n.(typed.Node).Type().Name()` as `Foo`; and if
// `n.GetBuilder().AmendMap().Insert(...)` is called with nodes of unmatching
// type given to the insertion, the builder will error!)
type NodeBuilder interface {
CreateMap() (MapBuilder, error)
AmendMap() (MapBuilder, error)
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment