lift

package module
v0.1.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 28, 2022 License: MIT Imports: 1 Imported by: 0

README

Go Reference

lift

Lift is a generics-inflected Go package for lifting Go types

Why lift?

Loosely (and imprecisely) inspired by notions of function lifting, lift intends to provide some glue for lifting in Go. The central idea of lift is type enumeration symbols - runtime values which predicate branching and matching logic over types.

This overlaps with a number of existing mechanisms in Go, from type assertions to reflect. lift is distinguished by being generics-inflected, and narrow in scope. Eventually, lift is trying to be a small but complete abstraction for runtime dispatch gadgetry. The package is at the moment entirely experimental in nature. I hope you find the results interesting or informative or useful :)

What is in lift?

Sym

Sym are type enumeration symbols. Additionally, Sym may carry wrapped values in conjunction with type enumertaion symbols. This puts a generics-inflected take on familiar ideas.

lift.Map

Type enumeration symbols are a natural fit as map keys. The Map type uses Sym keys, and may be parameterized to any V value. For example:

	piece, _ := lift.Load[ Queen ]( black )

loads a piece associated with some type Queen, from some map black.

Runtime dispatch gadgets

  • lift/conv has a Converter type, for indexing conversion functions. For example:
	rgb, _ := conv.To[RGB](cv, hex)

converting to some type RGB, from some hex value, with some converter cv.

  • lift package examples explore other runtime dispatch gadgetry.

How does lift work?

There are two types of type enumeration symbols at work in lift. First, the internal enum:

	type enum[T any] struct{}

Sensibly enough:

  • Any type T implies a corresponding type enum[T]
  • An enum[T] is == to all similarly flavored enum[T]
  • An enum[U] is not == to any differently flavored enum[V], for any V

Second, the exported Sym:

	type Sym interface { ... }

An enum[T] satisfies the Sym interface. When an enum[T] is boxed as a Sym, the comparison rules apply still apply, modulo the boxing. Concretely, if two Sym share a dynamic type enum[T], their dynamic values will never differ.

Not all Sym box an enum[T]. But, as an invariant promoted by the lift package, all (non-nil) Sym derive enum[T] via an internal method:

	enum() Sym

So, while a Sym is not type-parametric, it can propagate a type flavor for the purposes of comparison. In turn, this permits branching or matching on type flavors (even, or especially, where a type parameter associated with Sym flavor isn't present).

Is it efficient?

lift is not a performance hack. So far, performance hasn't been a priority; writing this package has been more about exploring the concepts. Examining performance more thoroughly is a future goal.

Glaringly, lift.Map is sluggish. In its defense, lift.Map is very general, and easy to write for. For some usages, a more performant alternative (e.g., a switch for fixed sets) is tractable. Something other than a built-in map for the underlying implementation of lift.Map could be interesting to explore.

That said, some preliminary benchmarking suggests that using lift as an alternative to equivalently elaborate schemes based on e.g. interface{} boxing is not a performance loss. Contrasting lift with reflect, unsafe etc. also seems worthwhile to look at.

Documentation

Overview

Package lift uses generic gadgets to lift types to values, referred to in documentation as type enumerations or type enumeration symbols. When lifted as values, type enumerations can predicate branching or matching logic over the universe of types.

The package intends to make production and consumption of type enumeration symbols narrow and ergonomic. The most fundamental component of lift is the Sym type, an exported type enumeraion symbol. See the README.md file for further exposition on internal mechanics.

The Map type is provided as an immediate and highly general application of type enumeration symbols, using them as map keys.

Function-flavored type enumerations (e.g. from func(T), distinct from T) can predicate runtime dispatch gadgetry. For example, the lift/conv package leverages lift to index conversion functions from given source and destination types. Package examples explore other gadgetry, roughly in order of increasing elaboration.

Example (A_fizzbuzz)

This example demonstrates lifting a function. If the lifted function is passed a Sym that doesn't match the desired type, the return is zero valued.

package main

import (
	"fmt"

	"github.com/AndrewHarrisSPU/lift"
)

// This example demonstrates lifting a function.
// If the lifted function is passed a [Sym] that doesn't match the desired type,
// the return is zero valued.
func main() {
	sayFizz := liftFizzBuzz(func(_ fizz) string {
		return "fizz"
	})
	sayBuzz := liftFizzBuzz(func(_ buzz) string {
		return "buzz"
	})

	for i := 1; i < 31; i++ {
		f, b := parseFizzBuzz(i)
		if res := sayFizz(f) + sayBuzz(b); res != "" {
			fmt.Println(i, res)
		}
	}
}

type fizz struct{}
type buzz struct{}

func liftFizzBuzz[T any](fn func(T) string) func(lift.Sym) string {
	return func(sym lift.Sym) string {
		if t, ok := lift.Unwrap[T](sym); ok {
			return fn(t)
		}
		return ""
	}
}

func parseFizzBuzz(i int) (f, b lift.Sym) {
	if i%3 == 0 {
		f = lift.Wrap(fizz{})
	}
	if i%5 == 0 {
		b = lift.Wrap(buzz{})
	}
	return
}
Output:

3 fizz
5 buzz
6 fizz
9 fizz
10 buzz
12 fizz
15 fizzbuzz
18 fizz
20 buzz
21 fizz
24 fizz
25 buzz
27 fizz
30 fizzbuzz
Example (B_fish)

This example demonstrates a Map with Sym values. The Map is a dispatch table; a Sym may be looked up, and a related function returned.

package main

import (
	"fmt"

	"github.com/AndrewHarrisSPU/lift"
)

func main() {
	// "recievers"
	type fish string

	type octopus struct {
		arms int
	}

	// "methods"
	fishSlap := func(sym lift.Sym) string {
		f := lift.MustUnwrap[fish](sym)
		return fmt.Sprintf("%sslap", f)
	}

	octopusSlap := func(sym lift.Sym) (slap string) {
		o := lift.MustUnwrap[octopus](sym)
		for i := 0; i < o.arms; i++ {
			slap += "octoslap"
		}
		return
	}

	// a dispatch map
	marineLifeSlap := lift.NewMap[func(lift.Sym) string](
		lift.Def[fish](fishSlap),
		lift.Def[octopus](octopusSlap),
	)

	// the demonstration:
	symbols := []lift.Sym{
		lift.Wrap(fish("trout")),
		lift.Wrap(fish("salmon")),
		lift.Wrap(octopus{8}),
	}

	for _, sym := range symbols {
		slap, _ := lift.LoadSym(marineLifeSlap, sym)
		fmt.Printf("boom! %s!\n", slap(sym))
	}
}
Output:

boom! troutslap!
boom! salmonslap!
boom! octoslapoctoslapoctoslapoctoslapoctoslapoctoslapoctoslapoctoslap!
Example (C_eventhandlers)

This example shows a switch-based dispatch table, yielding different logic than a Map would.

package main

import (
	"fmt"

	"github.com/AndrewHarrisSPU/lift"
)

// This example shows a switch-based dispatch table, yielding different logic than a [Map] would.
func main() {
	// brewing some dummy objects...
	photo := &file{path: "tableflip.jif", data: "(╯°□°)╯︵ ┻━┻"}
	home := &folder{path: "home", locked: false}
	sys := &folder{path: "system", locked: true}

	// brewing some dummy events...
	events := []event{
		newEvent(mouseClick{}, sys, lift.Empty{}),
		newEvent(mouseClick{}, photo, lift.Empty{}),
		newEvent(mouseDrop{}, photo, home),
		newEvent(mouseDrop{}, photo, sys),
		newEvent(mouseClick{}, home, lift.Empty{}),
	}

	for _, ev := range events {
		eventDispatch(ev)
	}
}

// GADGETS

type opFunc[OP any, SRC any, DST any] func(OP, SRC, DST) bool
type evFunc = func(event) bool

type event struct {
	op, src, dst lift.Sym
	signature    lift.Sym
}

func newEvent[OP any, SRC any, DST any](op OP, src SRC, dst DST) event {
	return event{
		op:        lift.Wrap(op),
		src:       lift.Wrap(src),
		dst:       lift.Wrap(dst),
		signature: lift.T[opFunc[OP, SRC, DST]](),
	}
}

func liftHandler[OP any, SRC any, DST any](fn opFunc[OP, SRC, DST]) evFunc {
	return func(ev event) bool {
		op, opOk := lift.Unwrap[OP](ev.op)
		src, srcOk := lift.Unwrap[SRC](ev.src)
		dst, dstOk := lift.Unwrap[DST](ev.dst)

		if !opOk || !srcOk || !dstOk {
			return false
		}
		return fn(op, src, dst)
	}
}

// DUMMY TYPES

// dummy mouse event types
type mouseClick struct{}
type mouseDrop struct{}

// dummy file types
type file struct {
	path, data string
}

type folder struct {
	path   string
	locked bool
	files  []*file
}

// HANDLERS & DISPATCH

func rejectLockedFolder(ev event) (rejected bool) {
	if dir, isDir := lift.Unwrap[*folder](ev.src); isDir && dir.locked {
		return true
	}
	if dir, isDir := lift.Unwrap[*folder](ev.dst); isDir && dir.locked {
		return true
	}
	return false
}

func openFile(op mouseClick, f *file, _ lift.Empty) bool {
	fmt.Printf("%s:\n\t%s\n", f.path, f.data)
	return true
}

func listFiles(op mouseClick, dir *folder, _ lift.Empty) bool {
	fmt.Printf("%s:\n", dir.path)
	for _, f := range dir.files {
		fmt.Println("\t", f.path)
	}
	return true
}

func moveFile(op mouseDrop, f *file, dir *folder) bool {
	dir.files = append(dir.files, f)
	return true
}

func eventDispatch(ev event) {
	switch {
	case rejectLockedFolder(ev):
	case liftHandler(openFile)(ev):
	case liftHandler(listFiles)(ev):
	case liftHandler(moveFile)(ev):
	}
}
Output:

tableflip.jif:
	(╯°□°)╯︵ ┻━┻
home:
	 tableflip.jif
Example (D_calculator)

This example emulates a pocket calculator, modeled as a finite state machine. Current state is maintained by a Map of transition functions. Inputs are parsed to Sym. The evaluaton loop takes one Sym, finds the associated edge in the calculator state, and dispatches that function.

package main

import (
	"fmt"

	"github.com/AndrewHarrisSPU/lift"
)

// This example emulates a pocket calculator, modeled as a finite state machine.
// Current state is maintained by a [Map] of transition functions. Inputs are parsed to [Sym].
// The evaluaton loop takes one [Sym], finds the associated edge in the calculator state, and
// dispatches that function.
func main() {
	calculate("1+2*3=-4=C/=-5C-56=7+8=9=")
	// 1+
	// >        1
	// 2*
	// >        3
	// 3=
	// >        9
	// -4=
	// >        5
	// C/
	// >        0
	// =
	// > DIVZERO!
	// -5C-
	// >        0
	// 56=
	// >      -56
	// 7+
	// >        7
	// 8=
	// >       15
	// 9=
	// >        9
}

func calculate(input string) {
	c := newCalculator()

	for _, r := range input {
		sym := parseCalculatorKey(r)
		edge, _ := lift.LoadSym(c.state, sym)
		edge(c, sym)
	}
}

// CALCULATOR

type calc struct {
	state    lift.Map[edgeFunc]
	acc, res int
	op
}

type edgeFunc = func(*calc, lift.Sym)
type op = func(*calc) error

func newCalculator() *calc {
	c := new(calc)
	c.state = lift.NewMap[edgeFunc](
		lift.Def[keyC](clear),
		lift.Def[keyEq](eq),
	)
	c.reset()
	c.enterStart()
	return c
}

// STATES

func (c *calc) enterStart() {
	c.state.Store(
		lift.Def[keyOp](eval),
		lift.Def[keyNum](beginAcc),
	)
}

func (c *calc) enterAccumulate() {
	c.state.Store(
		lift.Def[keyOp](eval),
		lift.Def[keyNum](acc),
	)
}

func (c *calc) enterEvaluated() {
	c.state.Store(
		lift.Def[keyOp](store),
		lift.Def[keyNum](resetAcc),
	)
}

func (c *calc) enterErr() {
	c.state.Store(
		lift.Def[keyOp](nop),
		lift.Def[keyNum](nop),
	)
}

// EDGES

func clear(c *calc, _ lift.Sym) {
	c.reset()
	c.enterStart()
}

func eq(c *calc, _ lift.Sym) {
	if err := c.evaluate(); err != nil {
		c.enterErr()
		return
	}
	c.enterEvaluated()
}

func eval(c *calc, sym lift.Sym) {
	if err := c.evaluate(); err != nil {
		c.enterErr()
		return
	}
	store(c, sym)
}

func store(c *calc, sym lift.Sym) {
	c.op = lift.MustUnwrap[keyOp](sym)
	c.enterStart()
}

func acc(c *calc, sym lift.Sym) {
	digit := lift.MustUnwrap[keyNum](sym)
	c.acc *= 10
	c.acc += digit
	c.enterAccumulate()
}

func beginAcc(c *calc, sym lift.Sym) {
	digit := lift.MustUnwrap[keyNum](sym)
	if digit == 0 {
		return
	}
	c.acc = 0
	acc(c, sym)
}

func resetAcc(c *calc, sym lift.Sym) {
	c.reset()
	beginAcc(c, sym)
}

func nop(c *calc, _ lift.Sym) {}

// METHODS

func (c *calc) evaluate() error {
	fmt.Print("\n> ")
	if err := c.op(c); err != nil {
		fmt.Println(err.Error())
		return err
	}
	fmt.Printf("%8d\n", c.res)
	return nil
}

func (c *calc) reset() {
	c.acc, c.res = 0, 0
	c.op = (*calc).add
}

func (c *calc) add() error {
	c.res += c.acc
	return nil
}

func (c *calc) sub() error {
	c.res -= c.acc
	return nil
}

func (c *calc) mul() error {
	c.res *= c.acc
	return nil
}

func (c *calc) div() error {
	if c.acc == 0 {
		return fmt.Errorf("DIVZERO!")
	}
	c.res /= c.acc
	return nil
}

// PARSING

type keyC struct{}
type keyEq struct{}
type keyOp = func(*calc) error
type keyNum = int

func parseCalculatorKey(r rune) lift.Sym {
	fmt.Printf("%c", r)

	switch r {
	case 'C':
		return lift.Wrap(keyC{})
	case '=':
		return lift.Wrap(keyEq{})
	case '+':
		return lift.Wrap((*calc).add)
	case '-':
		return lift.Wrap((*calc).sub)
	case '*':
		return lift.Wrap((*calc).mul)
	case '/':
		return lift.Wrap((*calc).div)
	case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
		return lift.Wrap(keyNum(r - '0'))
	default:
		return lift.Wrap(keyC{})
	}
}
Example (E_emptyAnyNil)

Corner cases of Sym around empty-ish or any-ish values are reasonable. Sym is an interface type, so the zero value of a Sym is nil-ish, and will cause panic.

package main

import (
	"fmt"

	"github.com/AndrewHarrisSPU/lift"
)

func main() {
	defer func() {
		if r := recover(); r != nil {
			fmt.Println("oops!", r)
		}
	}()

	items := lift.NewMap[string](
		lift.Def[struct{}]("the empty struct"),
		lift.Def[lift.Empty]("the lift.Empty struct"),
		lift.Def[any]("anything"),
		lift.Def[lift.Sym]("the type enumeration of lift.Sym"),
	)

	report := func(tag string, item string) {
		fmt.Printf("%-12s %s,\n", tag, item)
	}

	// Three kinds of the empty struct{}
	empty := struct{}{}
	item, _ := lift.LoadTypeOf(items, empty)
	report("empty i", item)

	item, _ = lift.Load[lift.Empty](items)
	report("empty ii", item)

	type local struct{}
	item, _ = lift.LoadTypeOf(items, local{})
	report("empty iii", item)

	// Three kinds of any
	item, _ = lift.Load[any](items)
	report("any i", item)

	item, _ = lift.LoadTypeOf(items, any("other thing"))
	report("any ii", item)

	item, _ = lift.LoadSym(items, lift.Any)
	report("any iii", item)

	// LoadTypeOf infers the type enumeration of lift.Sym from anything wrapped
	item, _ = lift.LoadTypeOf(items, lift.Wrap(any(nil)))
	report("sym i", item)

	// Doesn't crash yet, as we're passing the type enumeration of lift.Sym
	var NilSym lift.Sym
	item, _ = lift.LoadTypeOf(items, NilSym)
	report("sym ii", item)

	// PANIC ENSUES - we've passed a raw nil
	item, _ = lift.LoadSym(items, NilSym)
	report("nil i", item)

	// (unreached) PANIC ENSUES:
	items.Store(
		lift.DefSym(NilSym, "panic"),
	)

}
Output:

empty i      the empty struct,
empty ii     the lift.Empty struct,
empty iii    ,
any i        anything,
any ii       anything,
any iii      anything,
sym i        the type enumeration of lift.Sym,
sym ii       the type enumeration of lift.Sym,
oops! runtime error: invalid memory address or nil pointer dereference

Index

Examples

Constants

This section is empty.

Variables

View Source
var Any = enum[any]{}

Any is the type enumeration symbol of interface{}

Functions

func EnumIs

func EnumIs[T any](sym Sym) bool

EnumIs determines equivalence of two type enumerations: one derived from T, the other from the argument

Example
package main

import (
	"fmt"

	"github.com/AndrewHarrisSPU/lift"
)

func main() {
	a := lift.Wrap(false)
	fmt.Println(lift.EnumIs[bool](a))
}
Output:

true

func Load

func Load[K any, V any](m Map[V]) (v V, ok bool)

Load returns a value from a Map, if found.

Example
package main

import (
	"fmt"

	"github.com/AndrewHarrisSPU/lift"
)

func main() {
	masks := lift.NewMap[int](
		lift.Def[uint8](0xff),
	)

	mask, _ := lift.Load[uint8](masks)
	fmt.Println(mask&0x1234_5678 == 0x78)

	if _, ok := lift.Load[string](masks); !ok {
		fmt.Println("oops")
	}
}
Output:

true
oops

func LoadSym

func LoadSym[V any](m Map[V], sym Sym) (v V, ok bool)

LoadSym resembles Load, where the type enumeration key is lifted in the second argument.

Example
package main

import (
	"fmt"

	"github.com/AndrewHarrisSPU/lift"
)

func main() {
	type hammer struct{}
	type screwdriver struct{}

	tools := lift.NewMap[int](
		lift.Def[hammer](2),
		lift.Def[screwdriver](17),
	)

	var count int
	for _, tool := range tools.Keys() {
		n, _ := lift.LoadSym(tools, tool)
		count += n
	}

	fmt.Printf("I have %d tools", count)
}
Output:

I have 19 tools

func LoadTypeOf

func LoadTypeOf[K any, V any](m Map[V], _ K) (v V, ok bool)

LoadTypeOf resembles Load, with type parameter K inferred from a second argument.

Example
package main

import (
	"fmt"

	"github.com/AndrewHarrisSPU/lift"
)

func main() {
	fmtrs := lift.NewMap[string](
		lift.Def[uint8]("%08x"),
	)

	x := uint8(127)
	fmtr, _ := lift.LoadTypeOf(fmtrs, x)
	fmt.Printf(fmtr, x)
}
Output:

0000007f

func MustUnwrap

func MustUnwrap[T any](sym Sym) T

MustUnwrap is a fail-fast version of Unwrap. Unwrapping is expected to succed, and failure to unwrap panics.

func MustUnwrapAs

func MustUnwrapAs[T any](sym Sym) T

MustUnwrapAs is a fail-fast version of UnwrapAs. Unwrapping is expected to succed, and failure to unwrap panics.

func Unwrap

func Unwrap[T any](sym Sym) (t T, ok bool)

Unwrap recovers a value of type T. It is successful when the Sym to unwrap was produced by Wrap, and T precisely matches the flavor of T inferred by Wrap.

Example
package main

import (
	"fmt"

	"github.com/AndrewHarrisSPU/lift"
)

func main() {
	bits := byte(0)
	sym := lift.Wrap(bits)

	if _, ok := lift.Unwrap[byte](sym); ok {
		fmt.Print("got a byte")
	}
}
Output:

got a byte

func UnwrapAs

func UnwrapAs[T any](sym Sym) (t T, ok bool)

UnwrapAs resembles Unwrap, but is successful when the wrapped value satisfies an interface type T.

Example (Interface)

This works. A pointer to a strings.Builder is an io.Writer.

package main

import (
	"fmt"
	"io"
	"strings"

	"github.com/AndrewHarrisSPU/lift"
)

func main() {
	var b strings.Builder
	sym := lift.Wrap(&b)

	if _, ok := lift.UnwrapAs[io.Writer](sym); ok {
		fmt.Printf("unwrapped a Writer!")
	}
}
Output:

unwrapped a Writer!
Example (NoConversion)

This won't work. UnwrapAs performs type assertion, not conversion.

package main

import (
	"fmt"

	"github.com/AndrewHarrisSPU/lift"
)

func main() {
	type bit bool
	sym := lift.Wrap(bit(true))

	if bit, ok := lift.UnwrapAs[bool](sym); ok {
		fmt.Printf("%v\n", bit)
	} else {
		fmt.Println("?")
	}
}
Output:

?

Types

type Empty

type Empty struct{}

Empty is an empty structure

type Entry

type Entry[V any] struct {
	// contains filtered or unexported fields
}

Entry encapsulates a definition of a single Map association.

func Def

func Def[K any, V any](v V) Entry[V]

Def constructs Map entries.

Example
package main

import (
	"fmt"

	"github.com/AndrewHarrisSPU/lift"
)

func main() {
	type text struct{}
	type mauve struct{}

	crayons := lift.NewMap[int](
		lift.Def[text](0x222222),
		lift.Def[mauve](0xa17188),
	)

	headline, _ := lift.Load[mauve](crayons)
	paragraph, _ := lift.Load[text](crayons)

	fmt.Printf("#%06x, #%06x", headline, paragraph)
}
Output:

#a17188, #222222

func DefSym

func DefSym[V any](sym Sym, v V) Entry[V]

DefSym constructs Map entries. Unlike Def, the key flavor is already lifted in the Sym.

Example
package main

import (
	"fmt"

	"github.com/AndrewHarrisSPU/lift"
)

func main() {
	items := lift.NewMap[string](
		lift.DefSym(lift.Any, "could be anything"),
	)

	fish := lift.Wrap(any("it's a fish!"))
	anything, _ := lift.LoadSym(items, fish)

	fmt.Printf(anything)
}
Output:

could be anything

type Map

type Map[V any] struct {
	// contains filtered or unexported fields
}

Map defines associations between type enumerations and values of type V.

Example
package main

import (
	"fmt"

	"github.com/AndrewHarrisSPU/lift"
)

func main() {
	// Map creation
	m := lift.NewMap[string](
		lift.Def[int]("red"),
		lift.Def[float64]("blue"),
	)

	type vector2d [2]float64

	// Storing to a Map
	m.Store(
		lift.Def[complex128]("C"),
		lift.Def[vector2d]("R2"),
	)

	// Loading from a Map
	intColor, _ := lift.LoadTypeOf(m, 1)
	floatColor, _ := lift.LoadSym(m, lift.TypeOf(1.0))
	complexDomain, _ := lift.Load[complex128](m)
	vector2dDomain, _ := lift.Load[vector2d](m)

	fmt.Printf("ints are %s, float64s are %s\n", intColor, floatColor)
	fmt.Printf("complex128s live in %s (not %s)", complexDomain, vector2dDomain)
}
Output:

ints are red, float64s are blue
complex128s live in C (not R2)

func NewMap

func NewMap[V any](defs ...Entry[V]) Map[V]

NewMap returns an initialized Map, with any provided definitions stored.

func (Map[V]) Delete

func (m Map[V]) Delete(keys ...Sym)

Delete removes a variadic list of type enumerations from a Map.

Example
package main

import (
	"fmt"

	"github.com/AndrewHarrisSPU/lift"
)

func main() {
	m := lift.NewMap[bool](
		lift.Def[rune](true),
		lift.Def[string](true),
	)

	m.Delete(lift.T[rune]())
	if _, ok := lift.Load[rune](m); !ok {
		fmt.Println("No entry found")
	}
}
Output:

No entry found

func (Map[V]) Entries

func (m Map[V]) Entries() []Entry[V]

Entries collects definitions present in a Map.

func (Map[V]) Keys

func (m Map[V]) Keys() []Sym

Keys collects the type enumeration keys defined for a Map.

func (Map[V]) Len

func (m Map[V]) Len() int

Len returns the number of definitions present in a Map.

func (Map[V]) Store

func (m Map[V]) Store(defs ...Entry[V])

Store stores a variadic list of entries in a Map.

Example
package main

import (
	"fmt"

	"github.com/AndrewHarrisSPU/lift"
)

func main() {
	type Queen struct{}

	black := lift.NewMap[rune]()
	black.Store(
		lift.Def[Queen]('♛'),
	)

	piece, _ := lift.Load[Queen](black)
	fmt.Printf("%c", piece)
}
Output:

type Sym

type Sym interface {
	// contains filtered or unexported methods
}

A Sym is an interface protecting internal methods for producing type enumeration symbols.

func T

func T[T any]() Sym

The (function) T returns a (type) T-flavored type enumeration symbol.

Example
package main

import (
	"fmt"

	"github.com/AndrewHarrisSPU/lift"
)

func main() {
	r := lift.T[rune]()
	b := lift.T[byte]()

	fmt.Println(r == b)
}
Output:

false
Example (Alias)
package main

import (
	"fmt"

	"github.com/AndrewHarrisSPU/lift"
)

func main() {
	i := lift.T[int32]()
	r := lift.T[rune]()

	// because rune is an alias of int32, the output is "true"
	fmt.Println(i == r)

}
Output:

true

func TypeOf

func TypeOf[T any](T) Sym

TypeOf returns a T-flavored type enumeration symbol.

Example
package main

import (
	"fmt"

	"github.com/AndrewHarrisSPU/lift"
)

func main() {
	t := lift.T[string]()

	fmt.Println(t == lift.TypeOf(""))
	fmt.Println(t == lift.TypeOf("twine"))
	fmt.Println(t == lift.TypeOf([]rune{}))
}
Output:

true
true
false

func Wrap

func Wrap[T any](t T) Sym

Wrap produces a Sym, like T or TypeOf. Unlike T or TypeOf, the result may be unwrapped to recover a wrapped value.

Example
package main

import (
	"fmt"
	"math"

	"github.com/AndrewHarrisSPU/lift"
)

func main() {
	π := lift.Wrap(math.Pi)
	pi, _ := lift.Unwrap[float64](π)
	fmt.Println(pi == math.Pi)
}
Output:

true
Example (Equivalence)
package main

import (
	"fmt"

	"github.com/AndrewHarrisSPU/lift"
)

func main() {
	sym := lift.Wrap('?')

	// the wrapped symbol is not == to rune's type enumeration symbol
	fmt.Println(lift.T[rune]() == sym)

	// EnumIs[rune] evaluates true, however
	fmt.Println(lift.EnumIs[rune](sym))
}
Output:

false
true

Directories

Path Synopsis
The conv package provides a [Coverter], which indexes conversion functions.
The conv package provides a [Coverter], which indexes conversion functions.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL