Skip to content

Commit

Permalink
Setters and 0.2.0 prep (#17)
Browse files Browse the repository at this point in the history
* Setters

* Add array-based `concat` functions

* Clean up

  - remove extra concat
  - remove curried `over`, `set`, `mver`, `mut`
  - restore `prop`
  - add `mprop`

* small change
  • Loading branch information
mbrandonw authored May 15, 2018
1 parent b907805 commit 2583a58
Show file tree
Hide file tree
Showing 12 changed files with 978 additions and 465 deletions.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import Overture
import UIKit

// Base Styles

let autolayoutStyle = mut(\UIView.translatesAutoresizingMaskIntoConstraints, false)

let roundedStyle = concat(
mut(\UIView.clipsToBounds, true),
mut(\.layer.cornerRadius, 6)
)

func borderStyle(color: UIColor, width: CGFloat) -> (UIView) -> Void {
return concat(
mut(\UIView.layer.borderColor, color.cgColor),
mut(\.layer.borderWidth, width)
)
}

func buttonFont(_ font: UIFont) -> (UIButton) -> Void {
return { $0.titleLabel?.font = font }
}

func buttonTitle(_ title: String, for state: UIControlState = .normal) -> (UIButton) -> Void {
return { $0.setTitle(title, for: state) }
}

func buttonTitleColor(_ color: UIColor, for state: UIControlState = .normal) -> (UIButton) -> Void {
return { $0.setTitleColor(color, for: state) }
}

// App Styles

let baseButtonStyle: (UIButton) -> Void = concat(
mut(\.contentEdgeInsets, .init(top: 12, left: 16, bottom: 12, right: 16)),
buttonFont(.systemFont(ofSize: 16))
)

let roundButtonStyle = concat(
baseButtonStyle,
roundedStyle
)

let filledButtonStyle = concat(
roundButtonStyle,
mut(\.backgroundColor, .black),
mut(\.tintColor, .white)
)

let borderButtonStyle = concat(
roundButtonStyle,
borderStyle(color: .black, width: 2),
buttonTitleColor(.black)
)

let nextButton = with(
UIButton(),
concat(
filledButtonStyle,
buttonTitle("Next")
)
)

let cancelButton = with(
UIButton(),
concat(
borderButtonStyle,
buttonTitle("Cancel")
)
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import Foundation
import Overture

let guaranteeHeaders = over(\URLRequest.allHTTPHeaderFields) { $0 ?? [:] }

let setHeader = { name, value in
concat(
guaranteeHeaders,
set(compose(prop(\.allHTTPHeaderFields), map, prop(\.[name])), value)
)
}

let postJson = concat(
set(\.httpMethod, "POST"),
setHeader("Content-Type", "application/json; charset=utf-8")
)

let gitHubAccept = setHeader("Accept", "application/vnd.github.v3+json")

let attachAuthorization = { token in setHeader("Authorization", "Token " + token) }

let request = with(
URLRequest(url: URL(string: "https://www.pointfree.co/hello")!),
concat(
attachAuthorization("deadbeef"),
gitHubAccept,
postJson
)
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import Foundation
import Overture

let guaranteeHeaders = mver(\URLRequest.allHTTPHeaderFields) { $0 = $0 ?? [:] }

let setHeader = { name, value in
concat(guaranteeHeaders) { $0.allHTTPHeaderFields?[name] = value }
}

let postJson = concat(
mut(\.httpMethod, "POST"),
setHeader("Content-Type", "application/json; charset=utf-8")
)

let gitHubAccept = setHeader("Accept", "application/vnd.github.v3+json")

let attachAuthorization = { token in setHeader("Authorization", "Token " + token) }

let request = with(
URLRequest(url: URL(string: "https://www.pointfree.co/hello")!),
concat(
attachAuthorization("deadbeef"),
gitHubAccept,
postJson
)
)
9 changes: 8 additions & 1 deletion Overture.playground/contents.xcplayground
Original file line number Diff line number Diff line change
@@ -1,2 +1,9 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<playground version='6.0' target-platform='macos'/>
<playground version='6.0' target-platform='ios' executeOnSourceChanges='false'>
<pages>
<page name='Basics'/>
<page name='UIKit Setters'/>
<page name='URLRequest Setters - Immutable'/>
<page name='URLRequest Setters - Mutable'/>
</pages>
</playground>
299 changes: 151 additions & 148 deletions Overture.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

38 changes: 38 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,44 @@ with(User(name: "blob", age: 1), concat(
// User(name: "Blob", age: 2)
```

### `over` and `set`

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)).

The `over` function takes a `(Value) -> Value` transform function to modify an existing value.

``` swift
let celebrateBirthday = over(\User.age, incr)
// (User) -> User
```

The `set` function replaces an existing value with a brand new one.

```swift
with(user, set(\.name, "Blob"))
```

### `mprop`, `mver`, and `mut`

The `mprop`, `mver` and `mut` functions are _mutable_ variants of `prop`, `over` and `set`.

```swift
let guaranteeHeaders = mver(\URLRequest.allHTTPHeaderFields) { $0 = $0 ?? [:] }

let setHeader = { name, value in
concat(
guaranteeHeaders,
{ $0.allHTTPHeaderFields?[name] = value }
)
}

let request = with(URLRequest(url: url), concat(
mut(\.httpMethod, "POST"),
setHeader("Authorization", "Token " + token),
setHeader("Content-Type", "application/json; charset=utf-8")
))
```

## FAQ

- **Should I be worried about polluting the global namespace with free functions?**
Expand Down
126 changes: 101 additions & 25 deletions Sources/Overture/Concat.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,20 @@

/// Composes an array of functions that take and return the same type.
///
/// - Parameters:
/// - fs: Zero or more functions to apply in order.
/// - Returns: A new function that applies every function given as input in order.
/// - Note: This function is commonly seen in operator form as `<>`.
public func concat<A>(
_ fs: [(A) -> A]
)
-> (A) -> A {

return { (a: A) -> A in
fs.reduce(a) { a, f in f(a) }
}
}

/// Forward composition of functions that take and return the same type.
///
/// - Parameters:
Expand All @@ -13,8 +29,22 @@ public func concat<A>(
)
-> (A) -> A {

return { (a: A) -> A in
fz(fs.reduce(a) { a, f in f(a) })
return concat(fs + [fz])
}

/// Composes an array of throwing functions that take and return the same type.
///
/// - Parameters:
/// - fs: Zero or more functions to apply in order.
/// - Returns: A new function that applies every function given as input in order.
/// - Note: This function is commonly seen in operator form as `<>`.
public func concat<A>(
_ fs: [(A) throws -> A]
)
-> (A) throws -> A {

return { (a: A) throws -> A in
try fs.reduce(a) { a, f in try f(a) }
}
}

Expand All @@ -32,32 +62,26 @@ public func concat<A>(
)
-> (A) throws -> A {

return { (a: A) throws -> A in
try fz(fs.reduce(a) { a, f in try f(a) })
}
return concat(fs + [fz])
}

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

return { (a: inout A) -> Void in
fs.forEach { f in f(&a) }
fz(&a)
}
}


/// Forward, mutable value composition of throwing functions that take and return the same type.
/// Forward composition of mutable functions that mutate the same type.
///
/// - Parameters:
/// - fs: Zero or more functions to apply in order.
Expand All @@ -66,53 +90,105 @@ public func concat<A>(
/// - Returns: A new function that applies every function given as input in order.
/// - Note: This function is commonly seen in operator form as `<>`.
public func concat<A>(
_ fs: ((inout A) throws -> Void)...,
and fz: @escaping (_ a: inout A) throws -> Void = { _ in }
_ fs: ((inout A) -> Void)...,
and fz: @escaping (_ a: inout A) -> Void = { _ in }
)
-> (inout A) -> Void {

return concat(fs + [fz])
}

/// Composes an array of mutable, throwing functions that mutate the same type.
///
/// - Parameters:
/// - fs: Zero or more functions to apply in order.
/// - Returns: A new function that applies every function given as input in order.
/// - Note: This function is commonly seen in operator form as `<>`.
public func concat<A>(
_ fs: [(inout A) throws -> Void]
)
-> (inout A) throws -> Void {

return { (a: inout A) throws -> Void in
try fs.forEach { f in try f(&a) }
try fz(&a)
}
}

/// Forward, mutable reference composition of functions that take and return the same type.
/// Forward composition of mutable, throwing functions that mutate the same type.
///
/// - Parameters:
/// - fs: Zero or more functions to apply in order.
/// - fz: A final, optional, terminating function for trailing closure syntax.
/// - a: The argument to the final function.
/// - Returns: A new function that applies every function given as input in order.
/// - Note: This function is commonly seen in operator form as `<>`.
public func concat<A>(
_ fs: ((inout A) throws -> Void)...,
and fz: @escaping (_ a: inout A) throws -> Void = { _ in }
)
-> (inout A) throws -> Void {

return concat(fs + [fz])
}

/// Composes an array of reference-mutable functions that mutate the same type.
///
/// - Parameters:
/// - fs: Zero or more functions to apply in order.
/// - Returns: A new function that applies every function given as input in order.
/// - Note: This function is commonly seen in operator form as `<>`.
public func concat<A: AnyObject>(
_ fs: ((A) -> Void)...,
and fz: @escaping (_ a: A) -> Void = { _ in }
_ fs: [(A) -> Void]
)
-> (A) -> Void {

return { (a: A) -> Void in
fs.forEach { f in f(a) }
fz(a)
}
}

/// Forward, mutable reference composition of throwing functions that take and return the same type.
/// Forward composition of reference-mutable functions that mutate the same type.
///
/// - Parameters:
/// - fs: Zero or more functions to apply in order.
/// - fz: A final, optional, terminating function for trailing closure syntax.
/// - a: The argument to the final function.
/// - Returns: A new function that applies every function given as input in order.
/// - Note: This function is commonly seen in operator form as `<>`.
public func concat<A: AnyObject>(
_ fs: ((A) throws -> Void)...,
and fz: @escaping (_ a: A) throws -> Void = { _ in }
_ fs: ((A) -> Void)...,
and fz: @escaping (_ a: A) -> Void = { _ in }
)
-> (A) -> Void {

return concat(fs + [fz])
}

/// Composes an array of reference-mutable, throwing functions that mutate the same type.
///
/// - Parameters:
/// - fs: Zero or more functions to apply in order.
/// - Returns: A new function that applies every function given as input in order.
/// - Note: This function is commonly seen in operator form as `<>`.
public func concat<A: AnyObject>(
_ fs: [(A) throws -> Void]
)
-> (A) throws -> Void {

return { (a: A) throws -> Void in
try fs.forEach { f in try f(a) }
try fz(a)
}
}

/// Forward composition of reference-mutable, throwing functions that mutate the same type.
///
/// - Parameters:
/// - fs: Zero or more functions to apply in order.
/// - Returns: A new function that applies every function given as input in order.
/// - Note: This function is commonly seen in operator form as `<>`.
public func concat<A: AnyObject>(
_ fs: ((A) throws -> Void)...,
and fz: @escaping (_ a: A) throws -> Void = { _ in }
)
-> (A) throws -> Void {

return concat(fs + [fz])
}
Loading

0 comments on commit 2583a58

Please sign in to comment.