1// run -gcflags=-G=3
2
3// Copyright 2021 The Go Authors. All rights reserved.
4// Use of this source code is governed by a BSD-style
5// lice
6
7package main
8
9import "fmt"
10
11// Overriding the predeclare "any", so it can be used as a type constraint or a type
12// argument
13type any interface{}
14
15type Function[a, b any] interface {
16	Apply(x a) b
17}
18
19type incr struct{ n int }
20
21func (this incr) Apply(x int) int {
22	return x + this.n
23}
24
25type pos struct{}
26
27func (this pos) Apply(x int) bool {
28	return x > 0
29}
30
31type compose[a, b, c any] struct {
32	f Function[a, b]
33	g Function[b, c]
34}
35
36func (this compose[a, b, c]) Apply(x a) c {
37	return this.g.Apply(this.f.Apply(x))
38}
39
40type _Eq[a any] interface {
41	Equal(a) bool
42}
43
44type Int int
45
46func (this Int) Equal(that int) bool {
47	return int(this) == that
48}
49
50type List[a any] interface {
51	Match(casenil Function[Nil[a], any], casecons Function[Cons[a], any]) any
52}
53
54type Nil[a any] struct {
55}
56
57func (xs Nil[a]) Match(casenil Function[Nil[a], any], casecons Function[Cons[a], any]) any {
58	return casenil.Apply(xs)
59}
60
61type Cons[a any] struct {
62	Head a
63	Tail List[a]
64}
65
66func (xs Cons[a]) Match(casenil Function[Nil[a], any], casecons Function[Cons[a], any]) any {
67	return casecons.Apply(xs)
68}
69
70type mapNil[a, b any] struct {
71}
72
73func (m mapNil[a, b]) Apply(_ Nil[a]) any {
74	return Nil[b]{}
75}
76
77type mapCons[a, b any] struct {
78	f Function[a, b]
79}
80
81func (m mapCons[a, b]) Apply(xs Cons[a]) any {
82	return Cons[b]{m.f.Apply(xs.Head), Map[a, b](m.f, xs.Tail)}
83}
84
85func Map[a, b any](f Function[a, b], xs List[a]) List[b] {
86	return xs.Match(mapNil[a, b]{}, mapCons[a, b]{f}).(List[b])
87}
88
89func main() {
90	var xs List[int] = Cons[int]{3, Cons[int]{6, Nil[int]{}}}
91	var ys List[int] = Map[int, int](incr{-5}, xs)
92	var xz List[bool] = Map[int, bool](pos{}, ys)
93	cs1 := xz.(Cons[bool])
94	cs2 := cs1.Tail.(Cons[bool])
95	_, ok := cs2.Tail.(Nil[bool])
96	if cs1.Head != false || cs2.Head != true || !ok {
97		panic(fmt.Sprintf("got %v, %v, %v, expected false, true, true",
98			cs1.Head, cs2.Head, ok))
99	}
100}
101