Infix operators for monadic functions in Swift

Related tags

Utility Runes
Overview

Indecipherable symbols that some people claim have actual meaning.

pod Carthage compatible Swift Package Manager compatible

Please see the documentation for installation instructions.

What's included?

Importing Runes introduces several new operators and one global function that correspond to common Haskell typeclasses:

Functor

  • <^> (pronounced "map")

Applicative Functor

  • <*> (pronounced "apply")
  • <* (pronounced "left sequence")
  • *> (pronounced "right sequence")
  • pure (pronounced "pure")

Alternative

  • <|> (pronounced "alternate")
  • empty (pronounced "empty")

Monad

  • >>- (pronounced "flatMap") (left associative)
  • -<< (pronounced "flatMap") (right associative)
  • >-> (pronounced "Monadic compose") (left associative)
  • <-< (pronounced "Monadic compose") (right associative)

Implementations

We also include default implementations for Optional and Array with the following type signatures:

// Optional+Functor:
public func <^> <T, U>(f: T -> U, x: T?) -> U?

// Optional+Applicative:
public func <*> <T, U>(f: (T -> U)?, x: T?) -> U?
public func <* <T, U>(lhs: T?, rhs: U?) -> T?
public func *> <T, U>(lhs: T?, rhs: U?) -> U?
public func pure<T>(x: T) -> T?

// Optional+Alternative:
public func <|> <T>(lhs: T?, rhs: T?) -> T?
public func empty<T>() -> T?

// Optional+Monad:
public func >>- <T, U>(x: T?, f: T -> U?) -> U?
public func -<< <T, U>(f: T -> U?, x: T?) -> U?
public func >-> <T, U, V>(f: T -> U?, g: U -> V?) -> T -> V?
public func <-< <T, U, V>(f: U -> V?, g: T -> U?) -> T -> V?

// Array+Functor:
public func <^> <T, U>(f: T -> U, x: [T]) -> [U]

// Array+Applicative:
public func <*> <T, U>(fs: [T -> U], x: [T]) -> [U]
public func <* <T, U>(lhs: [T], rhs: [U]) -> [T]
public func *> <T, U>(lhs: [T], rhs: [U]) -> [U]
public func pure<T>(x: T) -> [T]

// Array+Alternative:
public func <|> <T>(lhs: [T], rhs: [T]) -> [T]
public func empty<T>() -> [T]

// Array+Monad:
public func >>- <T, U>(x: [T], f: T -> [U]) -> [U]
public func -<< <T, U>(f: T -> [U], x: [T]) -> [U]
public func >-> <T, U, V>(f: T -> [U], g: U -> [V]) -> T -> [V]
public func <-< <T, U, V>(f: U -> [V], g: T -> [U]) -> T -> [V]

// Result+Functor:
public func <^> <T, U, E>(f: (T) -> U, a: Result<T, E>) -> Result<U, E>

// Result+Applicative:
public func <*> <T, U, E>(f: Result<(T) -> U, E>, a: Result<T, E>) -> Result<U, E>
public func <* <T, U, E>(lhs: Result<T, E>, rhs: Result<U, E>) -> Result<T, E>
public func *> <T, U, E>(lhs: Result<T, E>, rhs: Result<U, E>) -> Result<U, E>
public func pure<T, E>(_ a: T) -> Result<T, E>

// Result+Alternative:
public func <|> <T, E>(lhs: Result<T, E>, rhs: @autoclosure () -> Result<T, E>) -> Result<T, E>

// Result+Monad:
public func >>- <T, U, E>(a: Result<T, E>, f: (T) -> Result<U, E>) -> Result<U, E>
public func -<< <T, U, E>(f: (T) -> Result<U, E>, a: Result<T, E>) -> Result<U, E>
public func >-> <T, U, V, E>(f: @escaping (T) -> Result<U, E>, g: @escaping (U) -> Result<V, E>) -> (T) -> Result<V, E>
public func <-< <T, U, V, E>(f: @escaping (U) -> Result<V, E>, g: @escaping (T) -> Result<U, E>) -> (T) -> Result<V, E>

Contributing

See the CONTRIBUTING document. Thank you, contributors!

License

Runes is Copyright (c) 2015 thoughtbot, inc. It is free software, and may be redistributed under the terms specified in the LICENSE file.

About

thoughtbot

Runes is maintained and funded by thoughtbot, inc. The names and logos for thoughtbot are trademarks of thoughtbot, inc.

We love open source software! See our other projects or look at our product case studies and hire us to help build your iOS app.

Comments
  • Move from Fox to SwiftCheck

    Move from Fox to SwiftCheck

    I really like SwiftCheck's syntax more then Fox. And this removes a metric fuckload of overhead, since it becomes the only dependency.

    Right now, this is pointed at master, because I need a new release of SwiftCheck that doesn't conflict with the operators we define.

    See https://github.com/typelift/SwiftCheck/issues/100 for more info.

    opened by gfontenot 14
  • Add Applicative `*>`, `<*`, and Alternative `<|>` operators

    Add Applicative `*>`, `<*`, and Alternative `<|>` operators

    This pull request adds new Applicative *>, <*, and Alternative <|> operators, which are needed feature in https://github.com/tryswift/TryParsec/pull/11 and all other functional libraries :sparkles: (See also: #65)

    By the way, Argo is currently using the same <|> operator in Alternative.swift#L13 with precedence 140, but this causes continuous monadic operations not working nicely. For example, m1 *> m2 <|> m3 *> m4 doesn't work and must use parenthesis to work around like (m1 *> m2) <|> (m3 *> m4), which is very cumbersome. Since Haskell uses lower precedence for <|>, I have dropped it down to precedence 120 (or, precedence 125 might be a better choice as discussed in https://github.com/tryswift/TryParsec/pull/11).

    I also added comments about Haskell's infix operator precedences for comparison with Runes :eyes:

    opened by inamiy 8
  • Curried functions to be removed in a later version of Swift

    Curried functions to be removed in a later version of Swift

    Greetings.

    Presently, I'm implementing Runes like so in my app:

    import Argo
    import Runes
    
    public struct Product {
        public let name: String
        public let price: NSDecimalNumber
        public let code: String
        public let balanceIncrease: Double?
        public let discount: NSDecimalNumber?
    
        public init(name: String, price: NSDecimalNumber, code: String, balanceIncrease: Double?, discount: NSDecimalNumber?) {
            self.name = name
            self.price = price
            self.code = code
            self.balanceIncrease = balanceIncrease
            self.discount = discount
        }
    }
    
    extension Product: Decodable {
        private static func create(name: String)(price: NSDecimalNumber)(code: String)(balanceIncrease: Double?)(discount: NSDecimalNumber?) -> Product {
            return Product(name: name, price: price, code: code, balanceIncrease: balanceIncrease, discount: discount)
        }
    
        public static func decode(json: JSON) -> Decoded<Product> {
            return Product.create
                <^> json <| "name"
                <*> json <| "price"
                <*> json <| "code"
                <*> json <|? "balance_increase"
                <*> json <|? "discount"
        }
    }
    
    extension Product: Equatable { }
    public func ==(lhs: Product, rhs: Product) -> Bool {
        return lhs.name == rhs.name && lhs.price == rhs.price && lhs.code == rhs.code && lhs.balanceIncrease == rhs.balanceIncrease
    }
    
    extension Product: CustomStringConvertible {
        public var description: String {
            return "Product <name: \(self.name), price: \(self.price), code: \(code), balanceIncrease: \(balanceIncrease)>"
        }
    }
    

    But in extension Product: Decodable I am making use of curried functions to achieve what I need. Apparently curried syntax is being removed, so going forward, would you have any suggestions on how I would achieve similar results without function currying?

    opened by asowers1 7
  • Various fixes

    Various fixes

    This includes a few things. Some minor (formatting, SwiftCheck update), some larger (Extension API across all targets, updates for newest version of Swift 3)

    Don't know how I feel about these precedence groups, tbh. I don't think I feel super great about them. Our tests are also completely broken, since it's apparently impossible to resolve the differences between how we're choosing to define these operators and how SwiftCheck defines them. No idea what to do about this.

    Fixes #59 Fixes #78

    opened by gfontenot 6
  • Monadic composition

    Monadic composition

    Monadic composition

    Here's how I'm using this with Result and RAC:

    request(.GET, "/myendpoint")
      .flatMap(.Concat, transform: SignalProducer.init • (parseJSON >-> decodeSomething))
    

    Basically, this really helps to avoid explicit closures everywhere, which I value for both readability and the avoidance of capturing strong references to self.


    The composition operator

    In the example above I'm using as the composition operator (which can be typed on US keyboards with Opt-8). I threw this into the specs for this PR, because I think it really helps their clarity.

    I'd love your feedback on:

    • Whether you agree about the improvement to the specs
    • What you think about adding this to the public API of Runes in a subsequent commit
    opened by sharplet 6
  • Prevent incorrect use of `flatMap`

    Prevent incorrect use of `flatMap`

    Swift will happily coerce functions of the type A -> B into functions of the type A -> B?. That means that using flatMap with a function that doesn't return an optional will compile. But then at runtime, you will run into weird behaviour that you don't expect, or worse, Swift will segfault.

    In order to prevent these problems from happening in the future, we're going to throw errors ourselves if flatMap is used incorrectly with a message letting the user know that the type of the function isn't correct. Using a default (but ultimately unused) implementation of map will push people in the right direction if they go digging.

    Credit to @mattrubin for the idea: https://github.com/thoughtbot/Argo/issues/34#issuecomment-70166588

    opened by gfontenot 6
  • What about default implementations for SequenceType?

    What about default implementations for SequenceType?

    Notes regarding <*>: It doesn't seem possible to create a generic type constraint over a sequence of A -> B, so this version uses [A -> B] as a workaround (which is no problem when combined with <^>, which returns an Array). i.e., Swift borks at this:

    func <*> <F: SequenceType, S: SequenceType, A, B where F.Generator.Element == A -> B, S.Generator.Element == A> (fs: F, source: S) -> [B] {
                                                                                  ^^^^^^ NOPE
    

    I didn't think too hard about style, naming, etc., that's all up for grabs! Just keen to know if you're into this idea.

    opened by sharplet 6
  • Adding tvOS support

    Adding tvOS support

    Rebased against your master branch. Built all schemes of Runes from a dependent library (Argo) to test, and that was successful.

    I recognize this will require developers to use Xcode 7.1, so just let me know if you've got different ideas about where this might want to go.

    opened by bruceflowers 5
  • Remove guards for `flatMap`

    Remove guards for `flatMap`

    Looks like Apple fixed the automatic coercion of T -> U into T -> U? that was causing us issues with flatMap. Since the compiler no longer allows this, we don't need to guard against it manually.

    enhancement help wanted 
    opened by gfontenot 5
  • Change SwiftCheck dependency as private

    Change SwiftCheck dependency as private

    It seems SwiftCheck has been introduced since v4.2.2, and it is affecting end users to always pull its unnecessary repository via Swift Package Manager:

    $ cd my-project-using-runes/
    $ swift build
    Updating https://github.com/thoughtbot/Runes.git
    Fetching [email protected]:typelift/SwiftCheck.git
    ...
    

    I have tried both Xcode 10 (Swift 5.0) and Xcode 11 Beta (Swift 5.1), but result was the same. It's probably due to Swift Package Manager still not able to handle test dependencies nicely.

    I think there needs some workaround e.g. https://github.com/pointfreeco/swift-snapshot-testing/issues/201 . What do you think?

    opened by inamiy 4
  • @autoclosure in Swift 5

    @autoclosure in Swift 5

    In Sources/Runes/Optional/Optional+Alternative.swift:14 an @autclosure block is passed as an argument. This is forbidden in Swift 5.

    From Swift 5 release notes:

    In Swift 5 mode, @autoclosure parameters can no longer be forwarded to @autoclosure arguments in another function call. Instead, you must explicitly call the function value with parentheses: (); the call itself is wrapped inside an implicit closure, guaranteeing the same behavior as in Swift 4 mode. (SR-5719) (37321597)

    Is there any solution for this?

    opened by madcato 4
  • Add a release script

    Add a release script

    I'd like to be able to automate the release of this lib, so that we don't forget to do things like add tags, release to CocoaPods, etc. We should script this.

    opened by gfontenot 0
Releases(v5.1.0)
  • v5.1.0(May 22, 2020)

    This small release adds support for Swift 5.2 tooling, which means that if you're using Swift 5.2 you'll no longer see our test dependencies downloaded locally when you're just trying to annoy your coworkers with illegible code like a normal person.

    Source code(tar.gz)
    Source code(zip)
  • v5.0.0(Jun 1, 2019)

    This release adds operator definitions for the new Result<T, E> type in Swift 5's stdlib. Accordingly, Runes 5.0 drops support for Swift versions lower than 5.0.

    Source code(tar.gz)
    Source code(zip)
  • v4.2.2(May 30, 2019)

    This is a bug fix release to fix a compiler error that occured under Swift 5.0 when we tried to pass an autoclosure directly to another argument expecting an autoclosure, which is now disallowed. (Thanks @madcato!) This change should be backwards compatible with previous Swift versions.

    Source code(tar.gz)
    Source code(zip)
  • v4.2.1(Mar 19, 2019)

  • v4.2.0(Mar 15, 2019)

  • v4.1.1(Apr 15, 2018)

    This updates the Xcode project (and some internal dependencies) so that no warnings are thrown by the Xcode project.

    This will almost certainly mean nothing to you, unless you import this project into your project, in which case this will mean everything

    Source code(tar.gz)
    Source code(zip)
  • v4.1.0(Jun 7, 2017)

    This release fleshes out the rest of the Applicative implementations for Optional and Array. This adds two new operators to the mix for each type:

    • <* performs an applicative operation, discarding the right hand value
    • *> performs an applicative operation, discarding the left hand value

    We're also adding full implementations for Alternative for both types:

    • <|> performs an alternative operation
    • empty constructs an empty value (basically the dual of pure)
    • Optional now has an .or instance method that acts as a named version of <|>.
    Source code(tar.gz)
    Source code(zip)
  • v4.0.1(Nov 5, 2016)

    4.0.1 fixes issues with building Runes via Swift Package Manager that were caused by a change in the way SPM expected test directories to be structured.

    Source code(tar.gz)
    Source code(zip)
  • v4.0.0(Oct 12, 2016)

  • v3.2.1(Jul 22, 2016)

    3.2.1 adds better support for Xcode 8 and Swift 2.3 by recording these preferences in the project file. This will be the last release to support Swift 2.X, and master will move forward with Swift 3.0 support.

    Source code(tar.gz)
    Source code(zip)
  • v3.2.0(Jan 15, 2016)

  • v3.1.0(Oct 30, 2015)

  • v3.0.0(Sep 18, 2015)

    • [NEW]: Swift 2.0 support
    • [NEW]: watchOS targets for Carthage and CocoaPods
    • [NEW]: Optional.apply and Array.apply are now publicly available instance methods
    Source code(tar.gz)
    Source code(zip)
  • v3.0.0-rc.2(Jul 15, 2015)

    This makes it easier to add Runes to your framework as a light weight internal dependency for introducing the operators for your own types.

    Source code(tar.gz)
    Source code(zip)
  • v3.0.0-rc.1(Jul 15, 2015)

  • v2.0.0(May 4, 2015)

    This release refines the precedence for our operators to bring them in line with Haskell's implementation.

    • The map (<^>) and apply (<*>) operators now have a precedence of 130. This gives them an equal precedence with Swift's built in equality operator (==)
    • The flatMap operators (>>- and -<<) now have a precedence of 100. This gives them a precedence higher than the assignment operator (=), but lower than the or operator (||).

    Note that this change flips the precedence of these operators. Previous releases gave flatMap a higher precedence than map and apply. Updating to 2.0.0 might require the addition/removal of parenthesis in your code to return to the intended behavior.

    Huge thanks to @sharplet for the research (and implementation) behind this change.

    Source code(tar.gz)
    Source code(zip)
  • v1.2.2(Mar 13, 2015)

  • v1.2.1(Mar 13, 2015)

    This release removes the implementations of flatMap for Optionals and Arrays. flatMap is now included in the standard library for both of these types, so we don't need to duplicate their implementations here.

    This release also adds a flipped operator for flatMap (-<<) and removes the guards around improper use of flatMap. These guards were needed to avoid segfaults in the compiler, but the compiler is now preventing the improper use of this operator itself.

    Source code(tar.gz)
    Source code(zip)
  • v1.1.2(Mar 13, 2015)

  • v1.2.0(Feb 16, 2015)

    This release is intended to be used with Swift 1.2. There are no actual changes since Runes 1.1.1, but the attached binary has been built with the Swift 1.2 compiler.

    This release will not work with Swift versions 1.2 beta 3 and beyond due to the introduction of flatMap into the standard library

    Source code(tar.gz)
    Source code(zip)
  • v1.1.1(Feb 16, 2015)

    This release adds a protective ~~spell~~ compiler annotation to ensure that flatMap isn't used where map should be used. This is to prevent the Swift compiler from "helping" and coercing functions of the type A -> B to A -> B? and causing a segfault.

    Source code(tar.gz)
    Source code(zip)
  • v1.1(Feb 16, 2015)

  • v1.0(Feb 16, 2015)

Owner
thoughtbot, inc.
We work with organizations of all sizes to design, develop, and grow their web and mobile products.
thoughtbot, inc.
Call Swift functions from Rust with ease!

swift-rs Call Swift functions from Rust with ease! Todo Swift class deallocation from rust (implementing Drop and using deallocate_{type} methods) Mor

null 69 Dec 30, 2022
A set of helper classes and functions in Swift

SwiftTools This is a set of tools written in Swift that add some sugar and some small functionality. Mainly meant for small projects and scripts, as a

Vinicius Vendramini 0 Dec 13, 2021
Useful functions and extensions for sorting in Swift

SwiftSortUtils Motivation This library takes a shot at making comparing and sorting in Swift more pleasant. It also allows you to reuse your old NSSor

Daniel Strittmatter 60 Sep 9, 2022
Functional data types and functions for any project

Swiftx Swiftx is a Swift library containing functional abstractions and extensions to the Swift Standard Library. Swiftx is a smaller and simpler way

TypeLift 219 Aug 30, 2022
Utility functions for validating IBOutlet and IBAction connections

Outlets Utility functions for validating IBOutlet and IBAction connections. About Outlets provides a set of functions which validate that IBOutlets ar

Ben Chatelain 129 May 2, 2022
DGLabelSize - Functions that calculate the size of uilabel based on different string lengths

DGLabelSize Functions that calculate the size of uilabel based on different stri

donggyu 5 Jun 10, 2022
BCSwiftTor - Opinionated pure Swift controller for Tor, including full support for Swift 5.5 and Swift Concurrency

BCSwiftTor Opinionated pure Swift controller for Tor, including full support for

Blockchain Commons, LLC — A “not-for-profit” benefit corporation 4 Oct 6, 2022
Swift Markdown is a Swift package for parsing, building, editing, and analyzing Markdown documents.

Swift Markdown is a Swift package for parsing, building, editing, and analyzing Markdown documents.

Apple 2k Dec 28, 2022
Swift-DocC is a documentation compiler for Swift frameworks and packages aimed at making it easy to write and publish great developer documentation.

Swift-DocC is a documentation compiler for Swift frameworks and packages aimed at making it easy to write and publish great developer docum

Apple 833 Jan 3, 2023
Cross-Platform, Protocol-Oriented Programming base library to complement the Swift Standard Library. (Pure Swift, Supports Linux)

SwiftFoundation Cross-Platform, Protocol-Oriented Programming base library to complement the Swift Standard Library. Goals Provide a cross-platform in

null 620 Oct 11, 2022
Swift - ✏️Swift 공부 저장소✏️

Swift 스위프트의 기초 1. Swift의 기본 2. 변수와 상수 [3. 데이터 타입 기본] [4. 데이터 타입 고급] 5. 연산자 6. 흐름 제어 7. 함수 8. 옵셔널 객체지향 프로그래밍과 스위프트 9. 구조체와 클래스 10. 프로퍼티와 메서드 11. 인스턴스 생

Jiwon 0 Mar 9, 2022
Swift-ndi - Swift wrapper around NewTek's NDI SDK

swift-ndi Swift wrapper around NewTek's NDI SDK. Make sure you extracted latest

Alessio Nossa 12 Dec 29, 2022
__.swift is a port of Underscore.js to Swift.

__.swift Now, __.swift is version 0.2.0! With the chain of methods, __.swift became more flexible and extensible. Documentation: http://lotz84.github.

Tatsuya Hirose 86 Jun 29, 2022
SNTabBarDemo-Swift - Cool TabBar With Swift

SNTabBarDemo-Swift Cool TabBar How To Use // MARK: - setup private func setu

iAnchor 3 Sep 29, 2022
Swift-when - Expression switch support in Swift

Swift When - supporting switch expressions in Swift! What is it? Basically, it a

Gordan Glavaš 7 Nov 24, 2022
Swift-compute-runtime - Swift runtime for Fastly Compute@Edge

swift-compute-runtime Swift runtime for Fastly Compute@Edge Getting Started Crea

Andrew Barba 57 Dec 24, 2022
Swift-HorizontalPickerView - Customizable horizontal picker view component written in Swift for UIKit/iOS

Horizontal Picker View Customizable horizontal picker view component written in

Afraz Siddiqui 8 Aug 1, 2022
swift-highlight a pure-Swift data structure library designed for server applications that need to store a lot of styled text

swift-highlight is a pure-Swift data structure library designed for server applications that need to store a lot of styled text. The Highlight module is memory-efficient and uses slab allocations and small-string optimizations to pack large amounts of styled text into a small amount of memory, while still supporting efficient traversal through the Sequence protocol.

kelvin 4 Aug 14, 2022
Sovran-Swift: Small, efficient, easy. State Management for Swift

Sovran-Swift: Small, efficient, easy. State Management for Swift

Segment 5 Jan 3, 2023