Skip to content

Commit a8594a2

Browse files
author
lel
committed
Update in light of golang/go#45639 breaking the implementation and constraints package existing now
1 parent 808bb63 commit a8594a2

File tree

4 files changed

+41
-67
lines changed

4 files changed

+41
-67
lines changed

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MIT License
22

3-
Copyright (c) 2021 lelysses
3+
Copyright (c) 2021-2022 lelysses
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

LICENSE-THIRD-PARTY

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
Third party code used by lesser along with associated licenses
22
===============================================================
33

4-
"Ordered" interface copied from the Type Parameters Proposal at
5-
https://go.googlesource.com/proposal/+/refs/heads/master/design/43651-type-parameters.md
4+
The constraints library, found at golang.org/x/exp/constraints,
5+
borrowed under the BSD 3-clause license as reproduced below:
66

7-
Under the LICENSE at https://go.googlesource.com/proposal/+/refs/heads/master/LICENSE
8-
reproduced here:
7+
Copyright (c) 2009 The Go Authors. All rights reserved.
98

10-
Copyright (c) 2014 The Go Authors. All rights reserved.
119
Redistribution and use in source and binary forms, with or without
1210
modification, are permitted provided that the following conditions are
1311
met:

README.md

Lines changed: 34 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,38 @@
11
# lesser
22

33

4-
## THIS IS BROKEN
5-
6-
Somewhere between type parameters entering `gotip` and `go1.18-rc1`, several things that were previously possible were made impossible. Most crucially, this includes that line 43 now causes the compiler to throw the error `cannot use a type parameter as RHS in type declaration`. I'm not sure what purpose prohibiting this behavior served, but it's probably for the best because I seriously don't think this is the best way to solve this problem. I much prefer Ian Lance Taylor's suggestion [here](https://github.com/golang/go/issues/47632#issuecomment-897168431), and I hope this is what the community eventually falls behind.
7-
8-
94
## What is this?
105

116
`lesser` defines a type-parameterized interface with one method, `Less`, which returns a boolean for whether the caller, of type `T`, is less than some other instance of `T`. This is blatantly stolen from [Robert Griesemer's talk at Gophercon 2020](https://www.youtube.com/watch?v=TborQFPY2IM) about the type parameters proposal. Probably more controversially, this library also defines a wrapper called `Basic` over the built-in numerical types, exposing the underlying `<` operator through this `Less` method. The reasoning for this follows.
127

138
## Why is this?
149

15-
In the near future the standard library will, in a `constraints` package, have something like this (from [the type parameters proposal](https://go.googlesource.com/proposal/+/refs/heads/master/design/43651-type-parameters.md#operations-based-on-type-sets)):
16-
17-
```go
18-
// Ordered is a type constraint that matches any ordered type.
19-
// An ordered type is one that supports the <, <=, >, and >= operators.
20-
type Ordered interface {
21-
~int | ~int8 | ~int16 | ~int32 | ~int64 |
22-
~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr |
23-
~float32 | ~float64 |
24-
~string
25-
}
26-
```
27-
28-
This is explicitly only suitable for built-in types since user-defined types (structs etc) may not respond to operators. Conversely, however, built-ins may not respond to methods. As a consequence, any sorting function or ordered collection must specify different versions (`BasicSort` vs `Sort`, `BasicHeap` vs `Heap`, etc) for built-in types and user-defined types. This is an untenable position, and makes the type parameters proposal insufficient and unworkable for a large swathe of the problems it exists to solve (anything involving ordering).
10+
In the near future, [the `constraints` package](https://pkg.go.dev/golang.org/x/exp/constraints) will be in the standard library. Constraints defines an constraint called `Ordered`, which matches all types that respond to the `<` operator. This is explicitly only suitable for built-in types since user-defined types (structs, etc) may not respond to operators. Conversely, however, built-ins may not respond to methods. As a consequence, any sorting function or ordered collection must specify different versions (`BasicSort` vs `Sort`, `BasicHeap` vs `Heap`, etc) for built-in types and user-defined types. This is an untenable position, and makes the type parameters proposal insufficient and unworkable for a large swathe of the problems it exists to solve (anything involving ordering).
2911

3012
There appear to be three different possible directions to go here:
3113

32-
1. Every time you want to write an ordered collection or a sorting function or anything depending on natural ordering functionality, you have to copy paste it and have one copy use `Lesser` as the constraint and the other use `Ordered` as the constraint. That's not a reasonable request to make. Without this library or something like it ***you are here***. As of the end of last year this was Robert Griesemer's suggested solution, and to the best of my knowledge it still is.
14+
1. Every time you want to write an ordered collection or a sorting function or anything depending on natural ordering functionality, you have to copy paste it and have one copy use `Lesser` as the constraint and the other use `Ordered` as the constraint. That's not a reasonable request to make. Without this library or something like it ***you are here***. As of the end of last year this was Robert Griesemer's suggested solution, and to the best of my knowledge it still is. It's unclear what the purpose of generics is if we're still going to be forced to use code generation or copy-pasting.
3315

3416
2. Write a library very much like this one, but which defines a series of wrappers for each of the built-in types, each of which has a `Less` method, so then when you want to use `int`s in an ordered collection you'd convert them all to `lesser.Int` first. This is scary and gives me serious Java heebie-jeebies. I do not want Go to turn into a world where we are all using special magic wrappers for every basic data type at all times or something.
3517

36-
3. Exactly what this library is, which is the same as the former but instead of defining the types manually, you create them at compile-time using type parameters with something like `lesser.Basic[int]` instead of `lesser.Int`. It feels far less likely this way that it'll develop into the scary situation I just described.
18+
3. Exactly what this library is, which is the same as (2) but instead of defining the types manually, you create them at compile-time using type parameters with something like `lesser.Basic[int]` instead of `lesser.Int`. It feels far less likely this way that it'll develop into the scary situation I just described.
19+
20+
4. [SEE ADDENDUM](#addendum) FOR IAN LANCE TAYLOR'S EPIC AND COOL ALTERNATIVE
3721

38-
I don't know if something like this is coming to the standard library, but I'm unwilling to wait until it is. I have a sneaking suspicion the `Lesser` interface itself is likely to make it into the `constraints` library alongside `Ordered`, at which point I'll likely remove each of them here. This doesn't provide a Java-style `compareTo` or the like, but merely exposes a `Less` method that is fit for sorting algorithms and ordered collections. Look elsewhere for something more robust. This is a plug for a hole in the current generics implementation. This is not a "framework"; this is not a "platform".
22+
I don't know if something like this is coming to the standard library, but I'm unwilling to wait until it is. For a while it felt like the `Lesser` interface itself was likely to make it into the `constraints` library, but I'm not sure about that now. All discussion of how the hell to actually use generics has been explicitly pushed until some time after generics are released into the wild. I hate the antichrist.
23+
24+
This doesn't provide a Java-style `compareTo` or the like, but merely exposes a `Less` method that is fit for sorting algorithms and ordered collections. Look elsewhere for something more robust. This is a plug for a hole in the current generics implementation. This is not a "framework"; this is not a "platform".
3925

4026
## How do I use it?
4127

42-
Well first you'll need to get Go 1.18, which might seem hard since it hasn't been released yet. You can get what amounts to the dev branch of the Go compiler using [`gotip`](https://pkg.go.dev/golang.org/dl/gotip):
28+
Well first you'll need to get Go 1.18, which has not yet been released. Release candidate 1 was released in February, and you can install it as follows:
4329

4430
```bash
45-
go install golang.org/dl/gotip@latest
46-
gotip download
31+
go install golang.org/dl/go1.18rc1@latest
32+
go1.18rc1 download
4733
```
4834

49-
Now you can use the `gotip` command as an alternative to the `go` command, but with generics enabled.
50-
51-
If you want to build this into your project, use `gotip` when updating or initializing your `go.mod`, or otherwise make sure that your `go.mod` notes that we're using `go 1.18`, and make sure you build or run with `gotip`, not `go`, or you'll get compiler syntax errors.
35+
Now you can use the `go1.18rc1` command as an alternative to the `go` command, but with generics enabled.
5236

5337
## Okay, but how do I *use* it?
5438

@@ -74,33 +58,41 @@ If you want to initialize the `Heap` we defined above for the built-in concrete
7458

7559
```go
7660
var h Heap[lesser.Basic[int]]
77-
h.Push(lesser.Basic[int](1))
78-
h.Push(lesser.Basic[int](2))
79-
h.Push(lesser.Basic[int](3))
61+
h.Push(lesser.Basic[int]{1})
62+
h.Push(lesser.Basic[int]{2})
63+
h.Push(lesser.Basic[int]{3})
8064
```
8165

82-
If you want to use it with your own wacky type, you won't need to (and can't) use `Basic`, since your own type won't implement `Ordered`. Instead, you can just do something like this:
66+
To get the value back out of a `Basic`, you just poll the `Val` attribute. It used to be as simple as a cast back to the correct type, but then [this issue](https://github.com/golang/go/issues/45639) happened because we can't have nice things, so `Basic` had to be changed from an alias type to a struct. The ergonomics have suffered as a consequence.
67+
68+
If you want to use it with your own wacky type, `Basic` doesn't apply, since your own type won't implement `Ordered`. Instead, you can just give it a `Less` method.
8369

8470
```go
85-
type Book struct {
86-
Title string
87-
ISBN uint64
71+
type Name struct {
72+
Last string
73+
First string
8874
}
8975

90-
func (b Book) Less(other Book) bool {
91-
return p.ISBN < other.ISBN
76+
func (this Name) Less(other Name) bool {
77+
if this.Last == other.Last {
78+
return this.First < other.First
79+
}
80+
return this.Last < other.Last
9281
}
9382
```
9483

95-
And now you should be able to directly push some `Book`s onto a heap of `Book`s.
84+
And now you should be able to directly push some `Name`s onto a heap of `Name`s.
9685

9786
```go
98-
var h Heap[Book]
99-
h.Push(Book{"Cool Book", 1234567890123})
100-
h.Push(Book{"Another Cool Book", 2345678901234})
101-
h.Push(Book{"And Still Another Cool Book", 3456789012345})
87+
var h Heap[Name]
88+
h.Push(Name{"Marx", "Karl"})
89+
h.Push(Name{"Marx", "Groucho"})
10290
```
10391

10492
## Okay, but really, why?
10593

10694
I don't know if I really think this library is a good idea. I don't know how Go-y it is. The definition of """idiomatic""" Go will probably change in the coming year, and maybe this is the way things are going. I don't know how I feel about that. More than anything I want this library to get other people thinking about how this is actually going to work, because without something like this library it's not possible to make simple ordered collections that operate on both user-defined and built-in data types.
95+
96+
## Addendum
97+
98+
Ian Lance Taylor made (what I think is) a great suggestion [here](https://github.com/golang/go/issues/47632#issuecomment-897168431), arguing for constraining containers to `any` and passing a comparison function to the constructor. This is a good idea and subjectively feels more idiomatic than what I have done here. I think?

lesser.go

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,7 @@
22
// method, Less, which returns a boolean for whether the caller, of type T,
33
// is less than some other instance of T. This is blatantly stolen from
44
// Robert Griesemer's talk at Gophercon about the type parameters proposal
5-
// in 2020. lesser also defines an Ordered constraint interface, which is
6-
// directly copied from the Type Parameters Proposal (for license, see
7-
// ./LICENSE-THIRD-PARTY).
5+
// in 2020.
86
//
97
// The library then defines a type-parameterized wrapper called "Basic"
108
// over all Ordered built-in types, allowing these types to implement
@@ -13,20 +11,6 @@
1311
// ordered collections. See the README for rationale.
1412
package lesser
1513

16-
// Ordered is a type constraint that matches any ordered type.
17-
// An ordered type is one that supports the <, <=, >, and >= operators.
18-
//
19-
// In the near future Ordered will exist in the standard library's
20-
// "constraints" package, but this is not in gotip yet. When it is, it
21-
// will be removed here, and Basic will be constrained by that
22-
// equivalent definition.
23-
type Ordered interface {
24-
~int | ~int8 | ~int16 | ~int32 | ~int64 |
25-
~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr |
26-
~float32 | ~float64 |
27-
~string
28-
}
29-
3014
// Interface is an interface that wraps the Less method.
3115
//
3216
// Less compares a caller of type T to some other variable of type T,
@@ -40,11 +24,11 @@ type Interface[T any] interface {
4024
// Ordered types (the set of Go built-in types which respond to the <
4125
// operator), and exposes this behavior via a Less method so that they
4226
// fall under the lesser.Interface constraint.
43-
type Basic[N Ordered] N
27+
type Basic[N constraints.Ordered] struct{ Val N }
4428

4529
// Less implements Interface[Basic[N]] for Basic[N]. Returns true if the value
4630
// of the caller is less than that of the parameter; otherwise returns
4731
// false.
4832
func (x Basic[N]) Less(y Basic[N]) bool {
49-
return x < y
33+
return x.Val < y.Val
5034
}

0 commit comments

Comments
 (0)