event: simplify Label

The separation of Label and Value was more API surface for no gain.

Change-Id: Idc52fe019953682318dd3ce3e5040d911039861a
Reviewed-on: https://go-review.googlesource.com/c/exp/+/329630
Trust: Ian Cottrell <iancottrell@google.com>
Run-TryBot: Ian Cottrell <iancottrell@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Jonathan Amsterdam <jba@google.com>
diff --git a/event/adapter/logfmt/logfmt.go b/event/adapter/logfmt/logfmt.go
index cc8b297..451d2de 100644
--- a/event/adapter/logfmt/logfmt.go
+++ b/event/adapter/logfmt/logfmt.go
@@ -48,17 +48,16 @@
 func (p *Printer) Event(w io.Writer, ev *event.Event) {
 	p.needSep = false
 	if !ev.At.IsZero() {
-		p.label(w, "time", event.BytesOf(ev.At.AppendFormat(p.buf[:0], TimeFormat)))
+		p.Label(w, event.Bytes("time", ev.At.AppendFormat(p.buf[:0], TimeFormat)))
 	}
 
 	if !p.SuppressNamespace && ev.Namespace != "" {
-		p.label(w, "in", event.StringOf(ev.Namespace))
+		p.Label(w, event.String("in", ev.Namespace))
 	}
 
 	if ev.Parent != 0 {
-		p.label(w, "parent", event.BytesOf(strconv.AppendUint(p.buf[:0], ev.Parent, 10)))
+		p.Label(w, event.Bytes("parent", strconv.AppendUint(p.buf[:0], ev.Parent, 10)))
 	}
-
 	for _, l := range ev.Labels {
 		if l.Name == "" {
 			continue
@@ -67,72 +66,79 @@
 	}
 
 	if ev.TraceID != 0 {
-		p.label(w, "trace", event.Uint64Of(ev.TraceID))
+		p.Label(w, event.Uint64("trace", ev.TraceID))
 	}
 
 	if ev.Message != "" {
-		p.label(w, "msg", event.StringOf(ev.Message))
+		p.Label(w, event.String("msg", ev.Message))
 	}
 
 	if ev.Name != "" {
-		p.label(w, "name", event.StringOf(ev.Name))
+		p.Label(w, event.String("name", ev.Name))
 	}
 
 	if ev.Kind == event.EndKind {
-		p.label(w, "end", event.Value{})
+		p.Label(w, event.Value("end", nil))
 	}
 
 	if ev.Error != nil {
-		p.label(w, "err", event.ValueOf(ev.Error))
+		p.Label(w, event.Value("err", ev.Error))
 	}
 
 	io.WriteString(w, "\n")
 }
 
 func (p *Printer) Label(w io.Writer, l event.Label) {
-	p.label(w, l.Name, l.Value)
-}
-
-func (p *Printer) Value(w io.Writer, v event.Value) {
-	switch {
-	case v.IsString():
-		s := v.String()
-		if p.QuoteValues || stringNeedQuote(s) {
-			p.quoteString(w, s)
-		} else {
-			io.WriteString(w, s)
-		}
-	case v.IsBytes():
-		buf := v.Bytes()
-		if p.QuoteValues || bytesNeedQuote(buf) {
-			p.quoteBytes(w, buf)
-		} else {
-			w.Write(buf)
-		}
-	case v.IsInt64():
-		w.Write(strconv.AppendInt(p.buf[:0], v.Int64(), 10))
-	case v.IsUint64():
-		w.Write(strconv.AppendUint(p.buf[:0], v.Uint64(), 10))
-	case v.IsFloat64():
-		w.Write(strconv.AppendFloat(p.buf[:0], v.Float64(), 'g', -1, 64))
-	case v.IsBool():
-		if v.Bool() {
-			io.WriteString(w, "true")
-		} else {
-			io.WriteString(w, "false")
-		}
-	default:
-		if p.w.Cap() == 0 {
-			// we rely on the inliner to cause this to not allocate
-			p.w = *bytes.NewBuffer(p.buf[:0])
-		}
-		fmt.Fprint(&p.w, v.Interface())
-		b := p.w.Bytes()
-		p.w.Reset()
-		if p.QuoteValues || bytesNeedQuote(b) {
-			p.quoteBytes(w, b)
-		} else {
-			w.Write(b)
+	if l.Name == "" {
+		return
+	}
+	if p.needSep {
+		io.WriteString(w, " ")
+	}
+	p.needSep = true
+	p.Ident(w, l.Name)
+	if l.HasValue() {
+		io.WriteString(w, "=")
+		switch {
+		case l.IsString():
+			s := l.String()
+			if p.QuoteValues || stringNeedQuote(s) {
+				p.quoteString(w, s)
+			} else {
+				io.WriteString(w, s)
+			}
+		case l.IsBytes():
+			buf := l.Bytes()
+			if p.QuoteValues || bytesNeedQuote(buf) {
+				p.quoteBytes(w, buf)
+			} else {
+				w.Write(buf)
+			}
+		case l.IsInt64():
+			w.Write(strconv.AppendInt(p.buf[:0], l.Int64(), 10))
+		case l.IsUint64():
+			w.Write(strconv.AppendUint(p.buf[:0], l.Uint64(), 10))
+		case l.IsFloat64():
+			w.Write(strconv.AppendFloat(p.buf[:0], l.Float64(), 'g', -1, 64))
+		case l.IsBool():
+			if l.Bool() {
+				io.WriteString(w, "true")
+			} else {
+				io.WriteString(w, "false")
+			}
+		default:
+			if p.w.Cap() == 0 {
+				// we rely on the inliner to cause this to not allocate
+				p.w = *bytes.NewBuffer(p.buf[:0])
+			}
+			fmt.Fprint(&p.w, l.Interface())
+			b := p.w.Bytes()
+			p.w.Reset()
+			if p.QuoteValues || bytesNeedQuote(b) {
+				p.quoteBytes(w, b)
+			} else {
+				w.Write(b)
+			}
 		}
 	}
 }
@@ -184,21 +190,6 @@
 	io.WriteString(w, `"`)
 }
 
-func (p *Printer) label(w io.Writer, name string, value event.Value) {
-	if name == "" {
-		return
-	}
-	if p.needSep {
-		io.WriteString(w, " ")
-	}
-	p.needSep = true
-	p.Ident(w, name)
-	if value.HasValue() {
-		io.WriteString(w, "=")
-		p.Value(w, value)
-	}
-}
-
 func stringNeedQuote(s string) bool {
 	if len(s) == 0 {
 		return true
diff --git a/event/adapter/logfmt/logfmt_test.go b/event/adapter/logfmt/logfmt_test.go
index be80ade..1be397d 100644
--- a/event/adapter/logfmt/logfmt_test.go
+++ b/event/adapter/logfmt/logfmt_test.go
@@ -62,80 +62,63 @@
 	}, {
 		name: "string",
 		event: event.Event{
-			Labels: []event.Label{{
-				Name:  "v1",
-				Value: event.StringOf("text"),
-			}, {
-				Name:  "v2",
-				Value: event.StringOf("text with quotes"),
-			}, {
-				Name:  "empty",
-				Value: event.StringOf(""),
-			}},
+			Labels: []event.Label{
+				event.String("v1", "text"),
+				event.String("v2", "text with quotes"),
+				event.String("empty", ""),
+			},
 		},
 		expect: `v1=text v2="text with quotes" empty=""`,
 	}, {
 		name: "int",
 		event: event.Event{
-			Labels: []event.Label{{
-				Name:  "value",
-				Value: event.Int64Of(67),
-			}},
+			Labels: []event.Label{
+				event.Int64("value", 67),
+			},
 		},
 		expect: `value=67`,
 	}, {
 		name: "float",
 		event: event.Event{
-			Labels: []event.Label{{
-				Name:  "value",
-				Value: event.Float64Of(263.2),
-			}},
+			Labels: []event.Label{
+				event.Float64("value", 263.2),
+			},
 		},
 		expect: `value=263.2`,
 	}, {
 		name: "bool",
 		event: event.Event{
-			Labels: []event.Label{{
-				Name:  "v1",
-				Value: event.BoolOf(true),
-			}, {
-				Name:  "v2",
-				Value: event.BoolOf(false),
-			}},
+			Labels: []event.Label{
+				event.Bool("v1", true),
+				event.Bool("v2", false),
+			},
 		},
 		expect: `v1=true v2=false`,
 	}, {
 		name: "value",
 		event: event.Event{
-			Labels: []event.Label{{
-				Name:  "v1",
-				Value: event.ValueOf(notString{"simple"}),
-			}, {
-				Name:  "v2",
-				Value: event.ValueOf(notString{"needs quoting"}),
-			}},
+			Labels: []event.Label{
+				event.Value("v1", notString{"simple"}),
+				event.Value("v2", notString{"needs quoting"}),
+			},
 		},
 		expect: `v1=simple v2="needs quoting"`,
 	}, {
 		name: "empty label",
 		event: event.Event{
-			Labels: []event.Label{{
-				Name: "before",
-			}, {
-				Name:  "",
-				Value: event.StringOf("text"),
-			}, {
-				Name: "after",
-			}},
+			Labels: []event.Label{
+				event.Value("before", nil),
+				event.String("", "text"),
+				event.Value("after", nil),
+			},
 		},
 		expect: `before after`,
 	}, {
 		name: "quoted ident",
 		event: event.Event{
-			Labels: []event.Label{{
-				Name:  "name with space",
-				Value: event.StringOf("text"),
-			}},
+			Labels: []event.Label{
+				event.String("name with space", "text"),
+			},
 		},
 		expect: `"name with space"=text`,
 	}, {
@@ -153,10 +136,9 @@
 	}, {
 		name: "quoting bytes",
 		event: event.Event{
-			Labels: []event.Label{{
-				Name:  "value",
-				Value: event.BytesOf(([]byte)(`bytes "need" quote`)),
-			}},
+			Labels: []event.Label{
+				event.Bytes("value", ([]byte)(`bytes "need" quote`)),
+			},
 		},
 		expect: `value="bytes \"need\" quote"`,
 	}} {
@@ -191,10 +173,9 @@
 		name:    "quote values",
 		printer: logfmt.Printer{QuoteValues: true},
 		event: event.Event{
-			Labels: []event.Label{{
-				Name:  "value",
-				Value: event.StringOf("text"),
-			}},
+			Labels: []event.Label{
+				event.String("value", "text"),
+			},
 		},
 		before: `value=text`,
 		after:  `value="text"`,
diff --git a/event/alloc_test.go b/event/alloc_test.go
index 80ce051..4e55395 100644
--- a/event/alloc_test.go
+++ b/event/alloc_test.go
@@ -17,8 +17,8 @@
 )
 
 func TestAllocs(t *testing.T) {
-	anInt := event.Label{Name: "int", Value: event.Int64Of(4)}
-	aString := event.Label{Name: "string", Value: event.StringOf("value")}
+	anInt := event.Int64("int", 4)
+	aString := event.String("string", "value")
 
 	e := event.NewExporter(logfmt.NewHandler(ioutil.Discard), &event.ExporterOptions{EnableNamespaces: true})
 	ctx := event.WithExporter(context.Background(), e)
diff --git a/event/common.go b/event/common.go
index b6876e3..34bfc8c 100644
--- a/event/common.go
+++ b/event/common.go
@@ -11,8 +11,8 @@
 )
 
 const (
-	MetricKey      = interfaceKey("metric")
-	MetricVal      = valueKey("metricValue")
+	MetricKey      = "metric"
+	MetricVal      = "metricValue"
 	DurationMetric = interfaceKey("durationMetric")
 )
 
@@ -78,22 +78,14 @@
 		// this was an end event, do we need to send a duration?
 		if v, ok := DurationMetric.Find(ev); ok {
 			//TODO: do we want the rest of the values from the end event?
-			v.(*Duration).Record(ctx, ev.At.Sub(ev.target.startTime))
+			v.(*DurationDistribution).Record(ctx, ev.At.Sub(ev.target.startTime))
 		}
 		ev.Deliver()
 	}
 }
 
-func (k valueKey) Of(v Value) Label {
-	return Label{Name: string(k), Value: v}
-}
-
-func (k valueKey) Find(ev *Event) (Value, bool) {
-	return lookupValue(string(k), ev.Labels)
-}
-
 func (k interfaceKey) Of(v interface{}) Label {
-	return Label{Name: string(k), Value: ValueOf(v)}
+	return Value(string(k), v)
 }
 
 func (k interfaceKey) Find(ev *Event) (interface{}, bool) {
@@ -105,11 +97,11 @@
 
 }
 
-func lookupValue(name string, labels []Label) (Value, bool) {
+func lookupValue(name string, labels []Label) (Label, bool) {
 	for i := len(labels) - 1; i >= 0; i-- {
 		if labels[i].Name == name {
-			return labels[i].Value, true
+			return labels[i], true
 		}
 	}
-	return Value{}, false
+	return Label{}, false
 }
diff --git a/event/event_test.go b/event/event_test.go
index 857c6c3..8c8ba35 100644
--- a/event/event_test.go
+++ b/event/event_test.go
@@ -207,12 +207,14 @@
 }
 
 type testTraceDurationHandler struct {
-	got event.Value
+	got event.Label
 }
 
 func (t *testTraceDurationHandler) Event(ctx context.Context, ev *event.Event) context.Context {
-	if ev.Kind == event.MetricKind {
-		t.got, _ = event.MetricVal.Find(ev)
+	for _, l := range ev.Labels {
+		if l.Name == event.MetricVal {
+			t.got = l
+		}
 	}
 	return ctx
 }
diff --git a/event/keys/keys.go b/event/keys/keys.go
index 574d696..a4463fb 100644
--- a/event/keys/keys.go
+++ b/event/keys/keys.go
@@ -12,11 +12,11 @@
 type Value string
 
 // From can be used to get a value from a Label.
-func (k Value) From(l event.Label) interface{} { return l.Value.Interface() }
+func (k Value) From(l event.Label) interface{} { return l.Interface() }
 
 // Of creates a new Label with this key and the supplied value.
 func (k Value) Of(v interface{}) event.Label {
-	return event.Label{Name: string(k), Value: event.ValueOf(v)}
+	return event.Value(string(k), v)
 }
 
 // Tag represents a key for tagging labels that have no value.
@@ -35,133 +35,133 @@
 
 // Of creates a new Label with this key and the supplied value.
 func (k Int) Of(v int) event.Label {
-	return event.Label{Name: string(k), Value: event.Int64Of(int64(v))}
+	return event.Int64(string(k), int64(v))
 }
 
 // From can be used to get a value from a Label.
-func (k Int) From(l event.Label) int { return int(l.Value.Int64()) }
+func (k Int) From(l event.Label) int { return int(l.Int64()) }
 
 // Int8 represents a key
 type Int8 string
 
 // Of creates a new Label with this key and the supplied value.
 func (k Int8) Of(v int8) event.Label {
-	return event.Label{Name: string(k), Value: event.Int64Of(int64(v))}
+	return event.Int64(string(k), int64(v))
 }
 
 // From can be used to get a value from a Label.
-func (k Int8) From(l event.Label) int8 { return int8(l.Value.Int64()) }
+func (k Int8) From(l event.Label) int8 { return int8(l.Int64()) }
 
 // Int16 represents a key
 type Int16 string
 
 // Of creates a new Label with this key and the supplied value.
 func (k Int16) Of(v int16) event.Label {
-	return event.Label{Name: string(k), Value: event.Int64Of(int64(v))}
+	return event.Int64(string(k), int64(v))
 }
 
 // From can be used to get a value from a Label.
-func (k Int16) From(l event.Label) int16 { return int16(l.Value.Int64()) }
+func (k Int16) From(l event.Label) int16 { return int16(l.Int64()) }
 
 // Int32 represents a key
 type Int32 string
 
 // Of creates a new Label with this key and the supplied value.
 func (k Int32) Of(v int32) event.Label {
-	return event.Label{Name: string(k), Value: event.Int64Of(int64(v))}
+	return event.Int64(string(k), int64(v))
 }
 
 // From can be used to get a value from a Label.
-func (k Int32) From(l event.Label) int32 { return int32(l.Value.Int64()) }
+func (k Int32) From(l event.Label) int32 { return int32(l.Int64()) }
 
 // Int64 represents a key
 type Int64 string
 
 // Of creates a new Label with this key and the supplied value.
 func (k Int64) Of(v int64) event.Label {
-	return event.Label{Name: string(k), Value: event.Int64Of(v)}
+	return event.Int64(string(k), v)
 }
 
 // From can be used to get a value from a Label.
-func (k Int64) From(l event.Label) int64 { return l.Value.Int64() }
+func (k Int64) From(l event.Label) int64 { return l.Int64() }
 
 // UInt represents a key
 type UInt string
 
 // Of creates a new Label with this key and the supplied value.
 func (k UInt) Of(v uint) event.Label {
-	return event.Label{Name: string(k), Value: event.Uint64Of(uint64(v))}
+	return event.Uint64(string(k), uint64(v))
 }
 
 // From can be used to get a value from a Label.
-func (k UInt) From(l event.Label) uint { return uint(l.Value.Uint64()) }
+func (k UInt) From(l event.Label) uint { return uint(l.Uint64()) }
 
 // UInt8 represents a key
 type UInt8 string
 
 // Of creates a new Label with this key and the supplied value.
 func (k UInt8) Of(v uint8) event.Label {
-	return event.Label{Name: string(k), Value: event.Uint64Of(uint64(v))}
+	return event.Uint64(string(k), uint64(v))
 }
 
 // From can be used to get a value from a Label.
-func (k UInt8) From(l event.Label) uint8 { return uint8(l.Value.Uint64()) }
+func (k UInt8) From(l event.Label) uint8 { return uint8(l.Uint64()) }
 
 // UInt16 represents a key
 type UInt16 string
 
 // Of creates a new Label with this key and the supplied value.
 func (k UInt16) Of(v uint16) event.Label {
-	return event.Label{Name: string(k), Value: event.Uint64Of(uint64(v))}
+	return event.Uint64(string(k), uint64(v))
 }
 
 // From can be used to get a value from a Label.
-func (k UInt16) From(l event.Label) uint16 { return uint16(l.Value.Uint64()) }
+func (k UInt16) From(l event.Label) uint16 { return uint16(l.Uint64()) }
 
 // UInt32 represents a key
 type UInt32 string
 
 // Of creates a new Label with this key and the supplied value.
 func (k UInt32) Of(v uint32) event.Label {
-	return event.Label{Name: string(k), Value: event.Uint64Of(uint64(v))}
+	return event.Uint64(string(k), uint64(v))
 }
 
 // From can be used to get a value from a Label.
-func (k UInt32) From(l event.Label) uint32 { return uint32(l.Value.Uint64()) }
+func (k UInt32) From(l event.Label) uint32 { return uint32(l.Uint64()) }
 
 // UInt64 represents a key
 type UInt64 string
 
 // Of creates a new Label with this key and the supplied value.
 func (k UInt64) Of(v uint64) event.Label {
-	return event.Label{Name: string(k), Value: event.Uint64Of(v)}
+	return event.Uint64(string(k), v)
 }
 
 // From can be used to get a value from a Label.
-func (k UInt64) From(l event.Label) uint64 { return l.Value.Uint64() }
+func (k UInt64) From(l event.Label) uint64 { return l.Uint64() }
 
 // Float32 represents a key
 type Float32 string
 
 // Of creates a new Label with this key and the supplied value.
 func (k Float32) Of(v float32) event.Label {
-	return event.Label{Name: string(k), Value: event.Float64Of(float64(v))}
+	return event.Float64(string(k), float64(v))
 }
 
 // From can be used to get a value from a Label.
-func (k Float32) From(l event.Label) float32 { return float32(l.Value.Float64()) }
+func (k Float32) From(l event.Label) float32 { return float32(l.Float64()) }
 
 // Float64 represents a key
 type Float64 string
 
 // Of creates a new Label with this key and the supplied value.
 func (k Float64) Of(v float64) event.Label {
-	return event.Label{Name: string(k), Value: event.Float64Of(v)}
+	return event.Float64(string(k), v)
 }
 
 // From can be used to get a value from a Label.
 func (k Float64) From(l event.Label) float64 {
-	return l.Value.Float64()
+	return l.Float64()
 }
 
 // String represents a key
@@ -169,19 +169,19 @@
 
 // Of creates a new Label with this key and the supplied value.
 func (k String) Of(v string) event.Label {
-	return event.Label{Name: string(k), Value: event.StringOf(v)}
+	return event.String(string(k), v)
 }
 
 // From can be used to get a value from a Label.
-func (k String) From(l event.Label) string { return l.Value.String() }
+func (k String) From(l event.Label) string { return l.String() }
 
 // Bool represents a key
 type Bool string
 
 // Of creates a new Label with this key and the supplied value.
 func (k Bool) Of(v bool) event.Label {
-	return event.Label{Name: string(k), Value: event.BoolOf(v)}
+	return event.Bool(string(k), v)
 }
 
 // From can be used to get a value from a Label.
-func (k Bool) From(l event.Label) bool { return l.Value.Bool() }
+func (k Bool) From(l event.Label) bool { return l.Bool() }
diff --git a/event/label.go b/event/label.go
index 1593cf5..57307cc 100644
--- a/event/label.go
+++ b/event/label.go
@@ -13,22 +13,12 @@
 	"unsafe"
 )
 
-// Value holds any value in an efficient way that avoids allocations for
-// most types.
-type Value struct {
-	packed  uint64
-	untyped interface{}
-}
-
 // Label is a named value.
 type Label struct {
-	Name  string
-	Value Value
-}
+	Name string
 
-// Equal reports whether two labels are equal.
-func (l1 Label) Equal(l2 Label) bool {
-	return l1.Name == l2.Name && l1.Value.Equal(l2.Value)
+	packed  uint64
+	untyped interface{}
 }
 
 // stringptr is used in untyped when the Value is a string
@@ -53,43 +43,46 @@
 type durationKind struct{}
 
 // HasValue returns true if the value is set to any type.
-func (v *Value) HasValue() bool { return v.untyped != nil }
+func (l Label) HasValue() bool { return l.untyped != nil }
 
-// Equal reports whether two values are equal.
-func (v1 *Value) Equal(v2 Value) bool {
-	if !v1.HasValue() {
-		return !v2.HasValue()
+// Equal reports whether two labels are equal.
+func (l Label) Equal(l2 Label) bool {
+	if l.Name != l2.Name {
+		return false
 	}
-	if !v2.HasValue() {
+	if !l.HasValue() {
+		return !l2.HasValue()
+	}
+	if !l2.HasValue() {
 		return false
 	}
 	switch {
-	case v1.IsString():
-		return v2.IsString() && v1.String() == v2.String()
-	case v1.IsInt64():
-		return v2.IsInt64() && v1.packed == v2.packed
-	case v1.IsUint64():
-		return v2.IsUint64() && v1.packed == v2.packed
-	case v1.IsFloat64():
-		return v2.IsFloat64() && v1.Float64() == v2.Float64()
-	case v1.IsBool():
-		return v2.IsBool() && v1.packed == v2.packed
-	case v1.IsDuration():
-		return v2.IsDuration() && v1.packed == v2.packed
+	case l.IsString():
+		return l2.IsString() && l.String() == l2.String()
+	case l.IsInt64():
+		return l2.IsInt64() && l.packed == l2.packed
+	case l.IsUint64():
+		return l2.IsUint64() && l.packed == l2.packed
+	case l.IsFloat64():
+		return l2.IsFloat64() && l.Float64() == l2.Float64()
+	case l.IsBool():
+		return l2.IsBool() && l.packed == l2.packed
+	case l.IsDuration():
+		return l2.IsDuration() && l.packed == l2.packed
 	default:
-		return v1.untyped == v2.untyped
+		return l.untyped == l2.untyped
 	}
 }
 
-// ValueOf returns a Value for the supplied value.
-func ValueOf(value interface{}) Value {
-	return Value{untyped: value}
+// Value returns a Label for the supplied value.
+func Value(name string, value interface{}) Label {
+	return Label{Name: name, untyped: value}
 }
 
 // Interface returns the value.
 // This will never panic, things that were not set using SetInterface will be
 // unpacked and returned anyway.
-func (v Value) Interface() interface{} {
+func (v Label) Interface() interface{} {
 	switch {
 	case v.IsString():
 		return v.String()
@@ -108,16 +101,16 @@
 	}
 }
 
-// StringOf returns a new Value for a string.
-func StringOf(s string) Value {
+// String returns a new Value for a string.
+func String(name string, s string) Label {
 	hdr := (*reflect.StringHeader)(unsafe.Pointer(&s))
-	return Value{packed: uint64(hdr.Len), untyped: stringptr(hdr.Data)}
+	return Label{Name: name, packed: uint64(hdr.Len), untyped: stringptr(hdr.Data)}
 }
 
 // String returns the value as a string.
 // This does not panic if v's Kind is not String, instead, it returns a string
 // representation of the value in all cases.
-func (v Value) String() string {
+func (v Label) String() string {
 	if sp, ok := v.untyped.(stringptr); ok {
 		var s string
 		hdr := (*reflect.StringHeader)(unsafe.Pointer(&s))
@@ -145,19 +138,19 @@
 }
 
 // IsString returns true if the value was built with StringOf.
-func (v Value) IsString() bool {
+func (v Label) IsString() bool {
 	_, ok := v.untyped.(stringptr)
 	return ok
 }
 
-// BytesOf returns a new Value for a string.
-func BytesOf(data []byte) Value {
+// Bytes returns a new Value for a string.
+func Bytes(name string, data []byte) Label {
 	hdr := (*reflect.SliceHeader)(unsafe.Pointer(&data))
-	return Value{packed: uint64(hdr.Len), untyped: bytesptr(hdr.Data)}
+	return Label{Name: name, packed: uint64(hdr.Len), untyped: bytesptr(hdr.Data)}
 }
 
 // Bytes returns the value as a bytes array.
-func (v Value) Bytes() []byte {
+func (v Label) Bytes() []byte {
 	bp, ok := v.untyped.(bytesptr)
 	if !ok {
 		panic("Bytes called on non []byte value")
@@ -171,19 +164,19 @@
 }
 
 // IsBytes returns true if the value was built with BytesOf.
-func (v Value) IsBytes() bool {
+func (v Label) IsBytes() bool {
 	_, ok := v.untyped.(bytesptr)
 	return ok
 }
 
-// Int64Of returns a new Value for a signed integer.
-func Int64Of(u int64) Value {
-	return Value{packed: uint64(u), untyped: int64Kind{}}
+// Int64 returns a new Value for a signed integer.
+func Int64(name string, u int64) Label {
+	return Label{Name: name, packed: uint64(u), untyped: int64Kind{}}
 }
 
 // Int64 returns the int64 from a value that was set with SetInt64.
 // It will panic for any value for which IsInt64 is not true.
-func (v Value) Int64() int64 {
+func (v Label) Int64() int64 {
 	if !v.IsInt64() {
 		panic("Int64 called on non int64 value")
 	}
@@ -191,19 +184,19 @@
 }
 
 // IsInt64 returns true if the value was built with SetInt64.
-func (v Value) IsInt64() bool {
+func (v Label) IsInt64() bool {
 	_, ok := v.untyped.(int64Kind)
 	return ok
 }
 
-// Uint64Of returns a new Value for an unsigned integer.
-func Uint64Of(u uint64) Value {
-	return Value{packed: u, untyped: uint64Kind{}}
+// Uint64 returns a new Value for an unsigned integer.
+func Uint64(name string, u uint64) Label {
+	return Label{Name: name, packed: u, untyped: uint64Kind{}}
 }
 
 // Uint64 returns the uint64 from a value that was set with SetUint64.
 // It will panic for any value for which IsUint64 is not true.
-func (v Value) Uint64() uint64 {
+func (v Label) Uint64() uint64 {
 	if !v.IsUint64() {
 		panic("Uint64 called on non uint64 value")
 	}
@@ -211,19 +204,19 @@
 }
 
 // IsUint64 returns true if the value was built with SetUint64.
-func (v Value) IsUint64() bool {
+func (v Label) IsUint64() bool {
 	_, ok := v.untyped.(uint64Kind)
 	return ok
 }
 
-// Float64Of returns a new Value for a floating point number.
-func Float64Of(f float64) Value {
-	return Value{packed: math.Float64bits(f), untyped: float64Kind{}}
+// Float64 returns a new Value for a floating point number.
+func Float64(name string, f float64) Label {
+	return Label{Name: name, packed: math.Float64bits(f), untyped: float64Kind{}}
 }
 
 // Float64 returns the float64 from a value that was set with SetFloat64.
 // It will panic for any value for which IsFloat64 is not true.
-func (v Value) Float64() float64 {
+func (v Label) Float64() float64 {
 	if !v.IsFloat64() {
 		panic("Float64 called on non float64 value")
 	}
@@ -231,22 +224,22 @@
 }
 
 // IsFloat64 returns true if the value was built with SetFloat64.
-func (v Value) IsFloat64() bool {
+func (v Label) IsFloat64() bool {
 	_, ok := v.untyped.(float64Kind)
 	return ok
 }
 
-// BoolOf returns a new Value for a bool.
-func BoolOf(b bool) Value {
+// Bool returns a new Value for a bool.
+func Bool(name string, b bool) Label {
 	if b {
-		return Value{packed: 1, untyped: boolKind{}}
+		return Label{Name: name, packed: 1, untyped: boolKind{}}
 	}
-	return Value{packed: 0, untyped: boolKind{}}
+	return Label{Name: name, packed: 0, untyped: boolKind{}}
 }
 
 // Bool returns the bool from a value that was set with SetBool.
 // It will panic for any value for which IsBool is not true.
-func (v Value) Bool() bool {
+func (v Label) Bool() bool {
 	if !v.IsBool() {
 		panic("Bool called on non bool value")
 	}
@@ -257,23 +250,23 @@
 }
 
 // IsBool returns true if the value was built with SetBool.
-func (v Value) IsBool() bool {
+func (v Label) IsBool() bool {
 	_, ok := v.untyped.(boolKind)
 	return ok
 }
 
-func DurationOf(d time.Duration) Value {
-	return Value{packed: uint64(d), untyped: durationKind{}}
+func Duration(name string, d time.Duration) Label {
+	return Label{Name: name, packed: uint64(d), untyped: durationKind{}}
 }
 
-func (v Value) Duration() time.Duration {
+func (v Label) Duration() time.Duration {
 	if !v.IsDuration() {
 		panic("Duration called on non-Duration value")
 	}
 	return time.Duration(v.packed)
 }
 
-func (v Value) IsDuration() bool {
+func (v Label) IsDuration() bool {
 	_, ok := v.untyped.(durationKind)
 	return ok
 }
diff --git a/event/label_test.go b/event/label_test.go
index 6617f61..7ca43a8 100644
--- a/event/label_test.go
+++ b/event/label_test.go
@@ -13,34 +13,34 @@
 
 func TestOfAs(t *testing.T) {
 	const i = 3
-	var v event.Value
-	v = event.Int64Of(i)
+	var v event.Label
+	v = event.Int64("key", i)
 	if got := v.Int64(); got != i {
 		t.Errorf("got %v, want %v", got, i)
 	}
-	v = event.Uint64Of(i)
+	v = event.Uint64("key", i)
 	if got := v.Uint64(); got != i {
 		t.Errorf("got %v, want %v", got, i)
 	}
-	v = event.Float64Of(i)
+	v = event.Float64("key", i)
 	if got := v.Float64(); got != i {
 		t.Errorf("got %v, want %v", got, i)
 	}
-	v = event.BoolOf(true)
+	v = event.Bool("key", true)
 	if got := v.Bool(); got != true {
 		t.Errorf("got %v, want %v", got, true)
 	}
 	const s = "foo"
-	v = event.StringOf(s)
+	v = event.String("key", s)
 	if got := v.String(); got != s {
 		t.Errorf("got %v, want %v", got, s)
 	}
 	tm := time.Now()
-	v = event.ValueOf(tm)
+	v = event.Value("key", tm)
 	if got := v.Interface(); got != tm {
 		t.Errorf("got %v, want %v", got, tm)
 	}
-	var vnil event.Value
+	var vnil event.Label
 	if got := vnil.Interface(); got != nil {
 		t.Errorf("got %v, want nil", got)
 	}
@@ -48,18 +48,18 @@
 
 func TestEqual(t *testing.T) {
 	var x, y int
-	vals := []event.Value{
+	vals := []event.Label{
 		{},
-		event.Int64Of(1),
-		event.Int64Of(2),
-		event.Uint64Of(3),
-		event.Uint64Of(4),
-		event.Float64Of(3.5),
-		event.Float64Of(3.7),
-		event.BoolOf(true),
-		event.BoolOf(false),
-		event.ValueOf(&x),
-		event.ValueOf(&y),
+		event.Int64("key", 1),
+		event.Int64("key", 2),
+		event.Uint64("key", 3),
+		event.Uint64("key", 4),
+		event.Float64("key", 3.5),
+		event.Float64("key", 3.7),
+		event.Bool("key", true),
+		event.Bool("key", false),
+		event.Value("key", &x),
+		event.Value("key", &y),
 	}
 	for i, v1 := range vals {
 		for j, v2 := range vals {
@@ -87,10 +87,10 @@
 		name string
 		f    func()
 	}{
-		{"int64", func() { event.Float64Of(3).Int64() }},
-		{"uint64", func() { event.Int64Of(3).Uint64() }},
-		{"float64", func() { event.Uint64Of(3).Float64() }},
-		{"bool", func() { event.Int64Of(3).Bool() }},
+		{"int64", func() { event.Float64("key", 3).Int64() }},
+		{"uint64", func() { event.Int64("key", 3).Uint64() }},
+		{"float64", func() { event.Uint64("key", 3).Float64() }},
+		{"bool", func() { event.Int64("key", 3).Bool() }},
 	} {
 		if !panics(test.f) {
 			t.Errorf("%s: got no panic, want panic", test.name)
@@ -100,15 +100,15 @@
 
 func TestString(t *testing.T) {
 	for _, test := range []struct {
-		v    event.Value
+		v    event.Label
 		want string
 	}{
-		{event.Int64Of(-3), "-3"},
-		{event.Uint64Of(3), "3"},
-		{event.Float64Of(.15), "0.15"},
-		{event.BoolOf(true), "true"},
-		{event.StringOf("foo"), "foo"},
-		{event.ValueOf(time.Duration(3 * time.Second)), "3s"},
+		{event.Int64("key", -3), "-3"},
+		{event.Uint64("key", 3), "3"},
+		{event.Float64("key", .15), "0.15"},
+		{event.Bool("key", true), "true"},
+		{event.String("key", "foo"), "foo"},
+		{event.Value("key", time.Duration(3*time.Second)), "3s"},
 	} {
 		if got := test.v.String(); got != test.want {
 			t.Errorf("%#v: got %q, want %q", test.v, got, test.want)
@@ -128,12 +128,12 @@
 		p = &i
 	)
 	a := int(testing.AllocsPerRun(5, func() {
-		i = event.Int64Of(1).Int64()
-		u = event.Uint64Of(1).Uint64()
-		f = event.Float64Of(1).Float64()
-		b = event.BoolOf(true).Bool()
-		s = event.StringOf("foo").String()
-		x = event.ValueOf(p).Interface()
+		i = event.Int64("key", 1).Int64()
+		u = event.Uint64("key", 1).Uint64()
+		f = event.Float64("key", 1).Float64()
+		b = event.Bool("key", true).Bool()
+		s = event.String("key", "foo").String()
+		x = event.Value("key", p).Interface()
 	}))
 	if a != 0 {
 		t.Errorf("got %d allocs, want zero", a)
diff --git a/event/metric.go b/event/metric.go
index 143a5a3..a71f1f1 100644
--- a/event/metric.go
+++ b/event/metric.go
@@ -60,12 +60,6 @@
 func (m *MetricDescriptor) Namespace() string   { return m.namespace }
 func (m *MetricDescriptor) Description() string { return m.description }
 
-// A MetricValue is a pair of a Metric and a Value.
-type MetricValue struct {
-	m Metric
-	v Value
-}
-
 // A Counter is a metric that counts something cumulatively.
 type Counter struct {
 	*MetricDescriptor
@@ -87,7 +81,7 @@
 func (c *Counter) Record(ctx context.Context, v uint64, labels ...Label) {
 	ev := New(ctx, MetricKind)
 	if ev != nil {
-		record(ev, c, Uint64Of(v))
+		record(ev, c, Uint64(MetricVal, v))
 		ev.Labels = append(ev.Labels, labels...)
 		ev.Deliver()
 	}
@@ -115,35 +109,35 @@
 func (g *FloatGauge) Record(ctx context.Context, v float64, labels ...Label) {
 	ev := New(ctx, MetricKind)
 	if ev != nil {
-		record(ev, g, Float64Of(v))
+		record(ev, g, Float64(MetricVal, v))
 		ev.Labels = append(ev.Labels, labels...)
 		ev.Deliver()
 	}
 }
 
-// A Duration records a distribution of durations.
+// A DurationDistribution records a distribution of durations.
 // TODO(generics): Distribution[T]
-type Duration struct {
+type DurationDistribution struct {
 	*MetricDescriptor
 }
 
 // NewDuration creates a new Duration with the given name.
-func NewDuration(name, description string) *Duration {
-	return &Duration{newMetricDescriptor(name, description)}
+func NewDuration(name, description string) *DurationDistribution {
+	return &DurationDistribution{newMetricDescriptor(name, description)}
 }
 
 // Descriptor returns the receiver's MetricDescriptor.
-func (d *Duration) Descriptor() *MetricDescriptor {
+func (d *DurationDistribution) Descriptor() *MetricDescriptor {
 	return d.MetricDescriptor
 }
 
 // Record converts its argument into a Value and returns a MetricValue with the
 // receiver and the value. It is intended to be used as an argument to
 // Builder.Metric.
-func (d *Duration) Record(ctx context.Context, v time.Duration, labels ...Label) {
+func (d *DurationDistribution) Record(ctx context.Context, v time.Duration, labels ...Label) {
 	ev := New(ctx, MetricKind)
 	if ev != nil {
-		record(ev, d, DurationOf(v))
+		record(ev, d, Duration(MetricVal, v))
 		ev.Labels = append(ev.Labels, labels...)
 		ev.Deliver()
 	}
@@ -170,12 +164,12 @@
 func (d *IntDistribution) Record(ctx context.Context, v int64, labels ...Label) {
 	ev := New(ctx, MetricKind)
 	if ev != nil {
-		record(ev, d, Int64Of(v))
+		record(ev, d, Int64(MetricVal, v))
 		ev.Labels = append(ev.Labels, labels...)
 		ev.Deliver()
 	}
 }
 
-func record(ev *Event, m Metric, v Value) {
-	ev.Labels = append(ev.Labels, MetricVal.Of(v), MetricKey.Of(ValueOf(m)))
+func record(ev *Event, m Metric, l Label) {
+	ev.Labels = append(ev.Labels, l, Value(MetricKey, m))
 }
diff --git a/event/otel/trace.go b/event/otel/trace.go
index 2c43edf..06883e6 100644
--- a/event/otel/trace.go
+++ b/event/otel/trace.go
@@ -44,11 +44,11 @@
 	for _, l := range ls {
 		switch l.Name {
 		case "link":
-			opts = append(opts, trace.WithLinks(l.Value.Interface().(trace.Link)))
+			opts = append(opts, trace.WithLinks(l.Interface().(trace.Link)))
 		case "newRoot":
 			opts = append(opts, trace.WithNewRoot())
 		case "spanKind":
-			opts = append(opts, trace.WithSpanKind(l.Value.Interface().(trace.SpanKind)))
+			opts = append(opts, trace.WithSpanKind(l.Interface().(trace.SpanKind)))
 		}
 	}
 	return opts
diff --git a/event/severity/severity.go b/event/severity/severity.go
index 64e1441..7d30cab 100644
--- a/event/severity/severity.go
+++ b/event/severity/severity.go
@@ -42,12 +42,12 @@
 
 // Of creates a label for the level.
 func (l Level) Label() event.Label {
-	return event.Label{Name: Key, Value: event.ValueOf(l)}
+	return event.Value(Key, l)
 }
 
 // From can be used to get a value from a Label.
 func From(t event.Label) Level {
-	return t.Value.Interface().(Level)
+	return t.Interface().(Level)
 }
 
 func (l Level) Log(ctx context.Context, msg string, labels ...event.Label) {