Skip to content

Commit 356fe57

Browse files
committed
Test: start implementing List
1 parent 41577b3 commit 356fe57

File tree

2 files changed

+210
-40
lines changed

2 files changed

+210
-40
lines changed

test/Inputs/Swiftskell.swift

Lines changed: 160 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ public protocol Show: ~Copyable {
88
borrowing func show() -> String
99
}
1010

11+
extension CustomStringConvertible {
12+
public func show() -> String { return description }
13+
}
14+
extension Int: Show {}
15+
1116
public func print(_ s: borrowing some Show & ~Copyable) {
1217
print(s.show())
1318
}
@@ -54,66 +59,48 @@ public protocol Generator: ~Copyable {
5459
public enum Pair<L: ~Copyable, R: ~Copyable>: ~Copyable {
5560
case elms(L, R)
5661
}
62+
extension Pair: Copyable where L: Copyable, R: Copyable {}
5763

5864
/// MARK: Data.Maybe
65+
public typealias Maybe<Wrapped: ~Copyable> = Optional<Wrapped>
5966

60-
public enum Maybe<Value: ~Copyable>: ~Copyable {
61-
case just(Value)
62-
case nothing
63-
}
64-
65-
extension Maybe: Copyable {}
66-
67-
extension Maybe: Show where Value: Show & ~Copyable {
67+
extension Maybe: Show where Wrapped: Show & ~Copyable {
6868
public borrowing func show() -> String {
6969
switch self {
70-
case let .just(elm):
70+
case let .some(elm):
7171
return elm.show()
72-
case .nothing:
72+
case .none:
7373
return "<nothing>"
7474
}
7575
}
7676
}
7777

78-
extension Maybe: Eq where Value: Eq, Value: ~Copyable {
78+
extension Maybe: Eq where Wrapped: Eq, Wrapped: ~Copyable {
7979
public static func ==(_ a: borrowing Self, _ b: borrowing Self) -> Bool {
8080
switch a {
81-
case let .just(a1):
81+
case let .some(a1):
8282
switch b {
83-
case let .just(b1):
83+
case let .some(b1):
8484
return a1 == b1
85-
case .nothing:
85+
case .none:
8686
return false
8787
}
88-
case .nothing:
88+
case .none:
8989
switch b {
90-
case .just:
90+
case .some:
9191
return false
92-
case .nothing:
92+
case .none:
9393
return true
9494
}
9595
}
9696
}
9797
}
9898

99-
100-
// FIXME: triggers crash!
101-
// @inlinable
102-
// public func fromMaybe<A: ~Copyable>(_ defaultVal: consuming A,
103-
// _ mayb: consuming Maybe<A>) -> A {
104-
// switch mayb {
105-
// case let .just(payload):
106-
// return payload
107-
// case .nothing:
108-
// return defaultVal
109-
// }
110-
// }
111-
11299
public func isJust<A: ~Copyable>(_ m: borrowing Maybe<A>) -> Bool {
113100
switch m {
114-
case .just:
101+
case .some:
115102
return true
116-
case .nothing:
103+
case .none:
117104
return false
118105
}
119106
}
@@ -127,3 +114,144 @@ public struct UnownedRef<Instance: AnyObject> {
127114
@usableFromInline
128115
internal unowned(unsafe) var _value: Instance
129116
}
117+
118+
/// Provides underlying support so that you can create recursive enums, because
119+
/// noncopyable enums do not yet support indirect cases.
120+
public struct Box<Wrapped: ~Copyable>: ~Copyable {
121+
private let _pointer: UnsafeMutablePointer<Wrapped>
122+
123+
init(_ wrapped: consuming Wrapped) {
124+
_pointer = .allocate(capacity: 1)
125+
_pointer.initialize(to: wrapped)
126+
}
127+
128+
deinit {
129+
_pointer.deinitialize(count: 1)
130+
_pointer.deallocate()
131+
}
132+
133+
consuming func take() -> Wrapped {
134+
let wrapped = _pointer.move()
135+
_pointer.deallocate()
136+
discard self
137+
return wrapped
138+
}
139+
140+
var borrow: Wrapped {
141+
_read { yield _pointer.pointee }
142+
}
143+
144+
consuming func map(_ transform: (consuming Wrapped) -> Wrapped) -> Self {
145+
_pointer.initialize(to: transform(_pointer.move()))
146+
return self
147+
}
148+
}
149+
150+
151+
/// MARK: Data.List
152+
public enum List<Element: ~Copyable>: ~Copyable {
153+
case cons(Element, Box<List<Element>>)
154+
case empty
155+
156+
public init(_ head: consuming Element,
157+
_ tail: consuming List<Element>) {
158+
self = .cons(head, Box(tail))
159+
}
160+
161+
public init() { self = .empty }
162+
}
163+
164+
/// Pure Iteration
165+
extension List where Element: ~Copyable {
166+
/// Performs forward iteration through the list, accumulating a result value.
167+
/// Returns f(xn,...,f(x2, f(x1, init))...), or init if the list is empty.
168+
public borrowing func foldl<Out>(
169+
init initial: consuming Out,
170+
_ f: (borrowing Element, consuming Out) -> Out) -> Out
171+
where Out: ~Copyable {
172+
func loop(_ acc: consuming Out, _ lst: borrowing Self) -> Out {
173+
switch lst {
174+
case .empty:
175+
return acc
176+
case let .cons(elm, tail):
177+
return loop(f(elm, acc), tail.borrow)
178+
}
179+
}
180+
return loop(initial, self)
181+
}
182+
183+
/// Performs reverse iteration through the list, accumulating a result value.
184+
/// Returns f(x1, f(x2,...,f(xn, init)...)) or init if the list is empty.
185+
public borrowing func foldr<Out>(
186+
init initial: consuming Out,
187+
_ f: (borrowing Element, consuming Out) -> Out) -> Out
188+
where Out: ~Copyable {
189+
switch self {
190+
case .empty:
191+
return initial
192+
case let .cons(elm, tail):
193+
return f(elm, tail.borrow.foldr(init: initial, f))
194+
}
195+
}
196+
197+
// Forward iteration without accumulating a result.
198+
public borrowing func forEach(_ f: (borrowing Element) -> Void) -> Void {
199+
switch self {
200+
case .empty: return
201+
case let .cons(elm, tail):
202+
f(elm)
203+
return tail.borrow.forEach(f)
204+
}
205+
}
206+
}
207+
208+
/// Initialization
209+
extension List where Element: ~Copyable {
210+
// Generates a list of elements [f(0), f(1), ..., f(n-1)] from left to right.
211+
// For n < 0, the empty list is created.
212+
public init(length n: Int, _ f: (Int) -> Element) {
213+
guard n > 0 else {
214+
self = .empty
215+
return
216+
}
217+
218+
let cur = n-1
219+
let elm = f(cur)
220+
self = List(elm, List(length: cur, f))
221+
}
222+
}
223+
224+
/// Basic utilities
225+
extension List where Element: ~Copyable {
226+
/// Is this list empty?
227+
public borrowing func empty() -> Bool {
228+
switch self {
229+
case .empty: return true
230+
case .cons(_, _): return false
231+
}
232+
}
233+
234+
/// How many elements are in this list?
235+
public borrowing func length() -> Int {
236+
return foldl(init: 0) { $1 + 1 }
237+
}
238+
239+
/// Pop the first element off the list, if present.
240+
public consuming func pop() -> Maybe<Pair<Element, List<Element>>> {
241+
switch consume self {
242+
case .empty: .none
243+
case let .cons(elm, tail): .elms(elm, tail.take())
244+
}
245+
}
246+
247+
/// Push an element onto the list.
248+
public consuming func push(_ newHead: consuming Element) -> List<Element> {
249+
return List(newHead, self)
250+
}
251+
}
252+
253+
extension List: Show where Element: Show & ~Copyable {
254+
public borrowing func show() -> String {
255+
return "[" + foldl(init: "]", { $0.show() + ", " + $1 })
256+
}
257+
}
Lines changed: 50 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,64 @@
11
// RUN: %empty-directory(%t)
22

3-
// RUN: %target-build-swift \
3+
// RUN: %target-build-swift-dylib(%t/%target-library-name(Swiftskell)) \
4+
// RUN: -module-name Swiftskell \
45
// RUN: -emit-module \
5-
// RUN: -emit-module-path %t \
6+
// RUN: -emit-module-path %t/Swiftskell.swiftmodule \
67
// RUN: -enable-library-evolution \
7-
// RUN: -module-name Swiftskell \
88
// RUN: -parse-as-library \
99
// RUN: %S/../Inputs/Swiftskell.swift \
1010
// RUN: -enable-experimental-feature SuppressedAssociatedTypes \
1111
// RUN: -enable-experimental-feature NonescapableTypes
1212

13-
// RUN: %target-build-swift %s -I %t -o %t/a.out
14-
// RUN: %target-codesign %t/a.out
15-
// RUN: %target-run %t/a.out | %FileCheck %s
13+
// RUN: %target-build-swift -I%t -L%t -lSwiftskell -parse-as-library %s \
14+
// RUN: -module-name E -o %t/E %target-rpath(%t)
15+
// RUN: %target-codesign %t/E
16+
// RUN: %target-codesign %t/%target-library-name(Swiftskell)
17+
// RUN: %target-run %t/E %t/%target-library-name(Swiftskell) | %FileCheck %s
1618

1719
// REQUIRES: executable_test
1820

1921
import Swiftskell
2022

21-
print("hello, world")
22-
// CHECK: hello, world
23+
/// assertion function
24+
func check(_ result: Bool, _ string: String? = nil, _ line: Int = #line) {
25+
if result { return }
26+
var msg = "assertion failure (line \(line))"
27+
if let extra = string {
28+
msg += ":\t" + extra
29+
}
30+
fatalError(msg)
31+
}
32+
33+
/// Basic noncopyable type for testing.
34+
struct File: ~Copyable, Show {
35+
let id: Int
36+
init(_ id: Int) {
37+
self.id = id
38+
}
39+
func show() -> String { return id.show() }
40+
}
41+
42+
43+
@main
44+
struct Main {
45+
static func main() {
46+
testListBasic()
47+
}
48+
}
49+
50+
func testListBasic() {
51+
var items = List<File>(length: 5) { .init($0) }
52+
print(items.show()) // CHECK: [0, 1, 2, 3, 4, ]
53+
check(items.length() == 5)
54+
check(!items.empty())
55+
56+
items = .empty
57+
check(items.length() == 0)
58+
check(items.empty())
59+
60+
let nums = List<Int>().push(7).push(7).push(3)
61+
print(nums.show()) // CHECK: [7, 7, 3, ]
62+
63+
64+
}

0 commit comments

Comments
 (0)