Skip to content

Commit 2583a58

Browse files
authored
Setters and 0.2.0 prep (#17)
* Setters * Add array-based `concat` functions * Clean up - remove extra concat - remove curried `over`, `set`, `mver`, `mut` - restore `prop` - add `mprop` * small change
1 parent b907805 commit 2583a58

File tree

12 files changed

+978
-465
lines changed

12 files changed

+978
-465
lines changed
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import Overture
2+
import UIKit
3+
4+
// Base Styles
5+
6+
let autolayoutStyle = mut(\UIView.translatesAutoresizingMaskIntoConstraints, false)
7+
8+
let roundedStyle = concat(
9+
mut(\UIView.clipsToBounds, true),
10+
mut(\.layer.cornerRadius, 6)
11+
)
12+
13+
func borderStyle(color: UIColor, width: CGFloat) -> (UIView) -> Void {
14+
return concat(
15+
mut(\UIView.layer.borderColor, color.cgColor),
16+
mut(\.layer.borderWidth, width)
17+
)
18+
}
19+
20+
func buttonFont(_ font: UIFont) -> (UIButton) -> Void {
21+
return { $0.titleLabel?.font = font }
22+
}
23+
24+
func buttonTitle(_ title: String, for state: UIControlState = .normal) -> (UIButton) -> Void {
25+
return { $0.setTitle(title, for: state) }
26+
}
27+
28+
func buttonTitleColor(_ color: UIColor, for state: UIControlState = .normal) -> (UIButton) -> Void {
29+
return { $0.setTitleColor(color, for: state) }
30+
}
31+
32+
// App Styles
33+
34+
let baseButtonStyle: (UIButton) -> Void = concat(
35+
mut(\.contentEdgeInsets, .init(top: 12, left: 16, bottom: 12, right: 16)),
36+
buttonFont(.systemFont(ofSize: 16))
37+
)
38+
39+
let roundButtonStyle = concat(
40+
baseButtonStyle,
41+
roundedStyle
42+
)
43+
44+
let filledButtonStyle = concat(
45+
roundButtonStyle,
46+
mut(\.backgroundColor, .black),
47+
mut(\.tintColor, .white)
48+
)
49+
50+
let borderButtonStyle = concat(
51+
roundButtonStyle,
52+
borderStyle(color: .black, width: 2),
53+
buttonTitleColor(.black)
54+
)
55+
56+
let nextButton = with(
57+
UIButton(),
58+
concat(
59+
filledButtonStyle,
60+
buttonTitle("Next")
61+
)
62+
)
63+
64+
let cancelButton = with(
65+
UIButton(),
66+
concat(
67+
borderButtonStyle,
68+
buttonTitle("Cancel")
69+
)
70+
)
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import Foundation
2+
import Overture
3+
4+
let guaranteeHeaders = over(\URLRequest.allHTTPHeaderFields) { $0 ?? [:] }
5+
6+
let setHeader = { name, value in
7+
concat(
8+
guaranteeHeaders,
9+
set(compose(prop(\.allHTTPHeaderFields), map, prop(\.[name])), value)
10+
)
11+
}
12+
13+
let postJson = concat(
14+
set(\.httpMethod, "POST"),
15+
setHeader("Content-Type", "application/json; charset=utf-8")
16+
)
17+
18+
let gitHubAccept = setHeader("Accept", "application/vnd.github.v3+json")
19+
20+
let attachAuthorization = { token in setHeader("Authorization", "Token " + token) }
21+
22+
let request = with(
23+
URLRequest(url: URL(string: "https://www.pointfree.co/hello")!),
24+
concat(
25+
attachAuthorization("deadbeef"),
26+
gitHubAccept,
27+
postJson
28+
)
29+
)
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import Foundation
2+
import Overture
3+
4+
let guaranteeHeaders = mver(\URLRequest.allHTTPHeaderFields) { $0 = $0 ?? [:] }
5+
6+
let setHeader = { name, value in
7+
concat(guaranteeHeaders) { $0.allHTTPHeaderFields?[name] = value }
8+
}
9+
10+
let postJson = concat(
11+
mut(\.httpMethod, "POST"),
12+
setHeader("Content-Type", "application/json; charset=utf-8")
13+
)
14+
15+
let gitHubAccept = setHeader("Accept", "application/vnd.github.v3+json")
16+
17+
let attachAuthorization = { token in setHeader("Authorization", "Token " + token) }
18+
19+
let request = with(
20+
URLRequest(url: URL(string: "https://www.pointfree.co/hello")!),
21+
concat(
22+
attachAuthorization("deadbeef"),
23+
gitHubAccept,
24+
postJson
25+
)
26+
)
Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,9 @@
11
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2-
<playground version='6.0' target-platform='macos'/>
2+
<playground version='6.0' target-platform='ios' executeOnSourceChanges='false'>
3+
<pages>
4+
<page name='Basics'/>
5+
<page name='UIKit Setters'/>
6+
<page name='URLRequest Setters - Immutable'/>
7+
<page name='URLRequest Setters - Mutable'/>
8+
</pages>
9+
</playground>

Overture.xcodeproj/project.pbxproj

Lines changed: 151 additions & 148 deletions
Large diffs are not rendered by default.

README.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,44 @@ with(User(name: "blob", age: 1), concat(
269269
// User(name: "Blob", age: 2)
270270
```
271271

272+
### `over` and `set`
273+
274+
The `over` and `set` functions produce `(Root) -> Root` transform functions that work on a `Value` in a structure given a key path (or [setter function](https://www.pointfree.co/episodes/ep7-setters-and-key-paths)).
275+
276+
The `over` function takes a `(Value) -> Value` transform function to modify an existing value.
277+
278+
``` swift
279+
let celebrateBirthday = over(\User.age, incr)
280+
// (User) -> User
281+
```
282+
283+
The `set` function replaces an existing value with a brand new one.
284+
285+
```swift
286+
with(user, set(\.name, "Blob"))
287+
```
288+
289+
### `mprop`, `mver`, and `mut`
290+
291+
The `mprop`, `mver` and `mut` functions are _mutable_ variants of `prop`, `over` and `set`.
292+
293+
```swift
294+
let guaranteeHeaders = mver(\URLRequest.allHTTPHeaderFields) { $0 = $0 ?? [:] }
295+
296+
let setHeader = { name, value in
297+
concat(
298+
guaranteeHeaders,
299+
{ $0.allHTTPHeaderFields?[name] = value }
300+
)
301+
}
302+
303+
let request = with(URLRequest(url: url), concat(
304+
mut(\.httpMethod, "POST"),
305+
setHeader("Authorization", "Token " + token),
306+
setHeader("Content-Type", "application/json; charset=utf-8")
307+
))
308+
```
309+
272310
## FAQ
273311

274312
- **Should I be worried about polluting the global namespace with free functions?**

Sources/Overture/Concat.swift

Lines changed: 101 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,20 @@
11

2+
/// Composes an array of functions that take and return the same type.
3+
///
4+
/// - Parameters:
5+
/// - fs: Zero or more functions to apply in order.
6+
/// - Returns: A new function that applies every function given as input in order.
7+
/// - Note: This function is commonly seen in operator form as `<>`.
8+
public func concat<A>(
9+
_ fs: [(A) -> A]
10+
)
11+
-> (A) -> A {
12+
13+
return { (a: A) -> A in
14+
fs.reduce(a) { a, f in f(a) }
15+
}
16+
}
17+
218
/// Forward composition of functions that take and return the same type.
319
///
420
/// - Parameters:
@@ -13,8 +29,22 @@ public func concat<A>(
1329
)
1430
-> (A) -> A {
1531

16-
return { (a: A) -> A in
17-
fz(fs.reduce(a) { a, f in f(a) })
32+
return concat(fs + [fz])
33+
}
34+
35+
/// Composes an array of throwing functions that take and return the same type.
36+
///
37+
/// - Parameters:
38+
/// - fs: Zero or more functions to apply in order.
39+
/// - Returns: A new function that applies every function given as input in order.
40+
/// - Note: This function is commonly seen in operator form as `<>`.
41+
public func concat<A>(
42+
_ fs: [(A) throws -> A]
43+
)
44+
-> (A) throws -> A {
45+
46+
return { (a: A) throws -> A in
47+
try fs.reduce(a) { a, f in try f(a) }
1848
}
1949
}
2050

@@ -32,32 +62,26 @@ public func concat<A>(
3262
)
3363
-> (A) throws -> A {
3464

35-
return { (a: A) throws -> A in
36-
try fz(fs.reduce(a) { a, f in try f(a) })
37-
}
65+
return concat(fs + [fz])
3866
}
3967

68+
/// Composes an array of mutable functions that mutate the same type.
4069
///
4170
/// - Parameters:
4271
/// - fs: Zero or more functions to apply in order.
43-
/// - fz: A final, optional, terminating function for trailing closure syntax.
44-
/// - a: The argument to the final function.
4572
/// - Returns: A new function that applies every function given as input in order.
4673
/// - Note: This function is commonly seen in operator form as `<>`.
4774
public func concat<A>(
48-
_ fs: ((inout A) -> Void)...,
49-
and fz: @escaping (_ a: inout A) -> Void = { _ in }
75+
_ fs: [(inout A) -> Void]
5076
)
5177
-> (inout A) -> Void {
5278

5379
return { (a: inout A) -> Void in
5480
fs.forEach { f in f(&a) }
55-
fz(&a)
5681
}
5782
}
5883

59-
60-
/// Forward, mutable value composition of throwing functions that take and return the same type.
84+
/// Forward composition of mutable functions that mutate the same type.
6185
///
6286
/// - Parameters:
6387
/// - fs: Zero or more functions to apply in order.
@@ -66,53 +90,105 @@ public func concat<A>(
6690
/// - Returns: A new function that applies every function given as input in order.
6791
/// - Note: This function is commonly seen in operator form as `<>`.
6892
public func concat<A>(
69-
_ fs: ((inout A) throws -> Void)...,
70-
and fz: @escaping (_ a: inout A) throws -> Void = { _ in }
93+
_ fs: ((inout A) -> Void)...,
94+
and fz: @escaping (_ a: inout A) -> Void = { _ in }
95+
)
96+
-> (inout A) -> Void {
97+
98+
return concat(fs + [fz])
99+
}
100+
101+
/// Composes an array of mutable, throwing functions that mutate the same type.
102+
///
103+
/// - Parameters:
104+
/// - fs: Zero or more functions to apply in order.
105+
/// - Returns: A new function that applies every function given as input in order.
106+
/// - Note: This function is commonly seen in operator form as `<>`.
107+
public func concat<A>(
108+
_ fs: [(inout A) throws -> Void]
71109
)
72110
-> (inout A) throws -> Void {
73111

74112
return { (a: inout A) throws -> Void in
75113
try fs.forEach { f in try f(&a) }
76-
try fz(&a)
77114
}
78115
}
79116

80-
/// Forward, mutable reference composition of functions that take and return the same type.
117+
/// Forward composition of mutable, throwing functions that mutate the same type.
81118
///
82119
/// - Parameters:
83120
/// - fs: Zero or more functions to apply in order.
84121
/// - fz: A final, optional, terminating function for trailing closure syntax.
85122
/// - a: The argument to the final function.
86123
/// - Returns: A new function that applies every function given as input in order.
87124
/// - Note: This function is commonly seen in operator form as `<>`.
125+
public func concat<A>(
126+
_ fs: ((inout A) throws -> Void)...,
127+
and fz: @escaping (_ a: inout A) throws -> Void = { _ in }
128+
)
129+
-> (inout A) throws -> Void {
130+
131+
return concat(fs + [fz])
132+
}
133+
134+
/// Composes an array of reference-mutable functions that mutate the same type.
135+
///
136+
/// - Parameters:
137+
/// - fs: Zero or more functions to apply in order.
138+
/// - Returns: A new function that applies every function given as input in order.
139+
/// - Note: This function is commonly seen in operator form as `<>`.
88140
public func concat<A: AnyObject>(
89-
_ fs: ((A) -> Void)...,
90-
and fz: @escaping (_ a: A) -> Void = { _ in }
141+
_ fs: [(A) -> Void]
91142
)
92143
-> (A) -> Void {
93144

94145
return { (a: A) -> Void in
95146
fs.forEach { f in f(a) }
96-
fz(a)
97147
}
98148
}
99149

100-
/// Forward, mutable reference composition of throwing functions that take and return the same type.
150+
/// Forward composition of reference-mutable functions that mutate the same type.
101151
///
102152
/// - Parameters:
103153
/// - fs: Zero or more functions to apply in order.
104-
/// - fz: A final, optional, terminating function for trailing closure syntax.
105-
/// - a: The argument to the final function.
106154
/// - Returns: A new function that applies every function given as input in order.
107155
/// - Note: This function is commonly seen in operator form as `<>`.
108156
public func concat<A: AnyObject>(
109-
_ fs: ((A) throws -> Void)...,
110-
and fz: @escaping (_ a: A) throws -> Void = { _ in }
157+
_ fs: ((A) -> Void)...,
158+
and fz: @escaping (_ a: A) -> Void = { _ in }
159+
)
160+
-> (A) -> Void {
161+
162+
return concat(fs + [fz])
163+
}
164+
165+
/// Composes an array of reference-mutable, throwing functions that mutate the same type.
166+
///
167+
/// - Parameters:
168+
/// - fs: Zero or more functions to apply in order.
169+
/// - Returns: A new function that applies every function given as input in order.
170+
/// - Note: This function is commonly seen in operator form as `<>`.
171+
public func concat<A: AnyObject>(
172+
_ fs: [(A) throws -> Void]
111173
)
112174
-> (A) throws -> Void {
113175

114176
return { (a: A) throws -> Void in
115177
try fs.forEach { f in try f(a) }
116-
try fz(a)
117178
}
118179
}
180+
181+
/// Forward composition of reference-mutable, throwing functions that mutate the same type.
182+
///
183+
/// - Parameters:
184+
/// - fs: Zero or more functions to apply in order.
185+
/// - Returns: A new function that applies every function given as input in order.
186+
/// - Note: This function is commonly seen in operator form as `<>`.
187+
public func concat<A: AnyObject>(
188+
_ fs: ((A) throws -> Void)...,
189+
and fz: @escaping (_ a: A) throws -> Void = { _ in }
190+
)
191+
-> (A) throws -> Void {
192+
193+
return concat(fs + [fz])
194+
}

0 commit comments

Comments
 (0)