Git Product home page Git Product logo

xebia-functional / second-bridge Goto Github PK

View Code? Open in Web Editor NEW
69.0 56.0 6.0 2.23 MB

Second Bridge is a Swift framework for functional programming. Our goal is to make Swift development on par with other functional languages like Scala by adding new data types, functions and operators.

Home Page: http://47degrees.github.io/second-bridge/

Swift 99.37% Ruby 0.27% Objective-C 0.36%
swift framework functional-programming data-types functions operators swiftz cocoapods

second-bridge's Introduction

Second Bridge

Second Bridge is a Swift framework for functional programming. Our goal is to make Swift development on par with other functional languages like Scala by adding new data types, functions and operators.

This project depends on the awesome Swiftz framework, that defines functional data structures, functions, idioms, and extensions that augment the Swift standard library.

Installation

Second Bridge supports the CocoaPods dependency manager. To install the framework in your project, place the following in your Podfile:

platform :ios, '8.0'
use_frameworks!

// (... other pods ...)
pod 'SecondBridge', :git => 'https://github.com/47deg/second-bridge.git'

By running pod install or pod update you should have Second Bridge in your workspace ready to go!

Features

DATA-TYPES PROTOCOLS

Traversable

Protocols like Traversable and Iterable will make it easier for you to expand the current data-types available. If you need to create a new data-type, just by implementing the following three methods your type will have access to the 40-something functions available in Second Bridge:

// Traverse all items of the instance, and call the provided function on each one.
func foreach(f: (ItemType) -> ())

// Build a new instance of the same Traversable type with the elements contained
// in the `elements` array (i.e.: returned from the **T functions).
class func build(elements: [ItemType]) -> Self 

// Build a new instance of the same Traversable type with the elements contained in the provided
// Traversable instance. Users calling this function are responsible of transforming the data of each
// item to a valid ItemType suitable for the current Traversable class. 
class func buildFromTraversable<U where U : Traversable>(traversable: U) -> Self

The following global functions are available for any Traversable-conforming type. Those are based on the main ones available in Scala for Traversable-derived types:

  • collectT: Returns an array containing the results of mapping a partial function f over a set of the elements of this Traversable that match the condition defined in f's isDefinedAt.
  • countT: Returns the number of elements of this Traversable satisfy the given predicate.
  • dropT: Selects all elements except the first n ones.
  • dropRightT: Selects all elements except the last n ones.
  • dropWhileT: Drops the longest prefix of elements that satisfy a predicate.
  • existsT: Returns true if at least one of its elements of this Traversable satisfy the given predicate.
  • findT: Returns the first element of this Traversable that satisfy the given predicate p, if any.
  • filterT: Returns a Traversable containing all the values from the current traversable that satisfy the includeElement closure.
  • filterNotT: Returns an array containing all the values from the current traversable except those that satisfy the excludeElement closure.
  • flatMapT: Returns the result of applying transform on each element of the traversable, and then flattening the results into an array. You can create a new Traversable from the results of the flatMap application by calling function Traversable.build and passing its results to it.
  • foldLeftT: Returns the result of repeatedly calling combine with an accumulated value initialized to initial and each element of the current traversable from right to left. A reversal equivalent to reduceT/foldLeftT.
  • foldRightT: Returns the result of repeatedly calling combine with an accumulated value initialized to initial and each element of the current traversable from left to right. Equivalent to reduceT.
  • forAllT: Returns true if all the elements of this Traversable satisfy the given predicate.
  • groupByT: Partitions this Traversable into a map of Traversables according to some discriminator function defined by the function f.
  • headT: Returns the first element of the Traversable or a nil if it's empty.
  • initT: Returns all the elements of this Traversable except the last one.
  • isEmptyT: Returns true if this Traversable doesn't contain any elements.
  • lastT: Returns the last element of the Traversable or a nil if it's empty.
  • mapT: Returns an array containing the results of mapping transform over the elements of the provided Traversable.
  • mapConserveT: Returns a traversable containing the results of mapping transform over its elements. The resulting elements are guaranteed to be the same type as the items of the provided traversable.
  • mkStringT: Returns a string representation of all the elements within the Traversable, separated by the provided separator and enclosed by the start and end strings.
  • nonEmptyT: Returns true if this Traversable contains elements.
  • partitionT: Returns a tuple containing the results of splitting the Traversable according to a predicate. The first traversable in the tuple contains those elements which satisfy the predicate, while the second contains those which don't.
  • reduceT: Returns the result of repeatedly calling combine with an accumulated value initialized to initial and each element of the current traversable.
  • reverseT: Returns a traversable with elements in inverse order.
  • sizeT: Returns the number of elements contained in this Traversable.
  • sliceT: Returns a Traversable made of the elements from source which satisfy the invariant: from <= indexOf(x) < until.
  • sortWithT: Returns a new Traversable containing all the elements from the provided one, but sorted by a predicate p.
  • spanT: Returns a tuple containing the results of splitting the Traversable according to a predicate. The first traversable in the tuple contains the first elements that satisfy the predicate p, while the second contains all elements after those.
  • splitAtT: Returns a tuple containing the results of splitting the Traversable at the given position.
  • tailT: Returns a new Traversable containing all the elements of the provided one except for the first element.
  • takeT: Returns a new Traversable of the same type containing the first n elements.
  • takeRightT: Returns a new Traversable containing the last n elements.
  • takeWhileT: Takes the longest prefix of elements that satisfy a predicate.
  • toArrayT: Returns a standard array containing the elements of this Traversable.
  • toListT: Returns a singly-linked list containing the elements of this Traversable.
  • unionT: Returns a new Traversable containing all the elements from the two provided Traversables.

Iterable

Any Traversable-conforming data type that also implements the standard SequenceType protocol (defining an iterator to step one-by-one through the collection's elements) instantly becomes an Iterable. Iterables have instant access to the next functions, also based on their Scala counter-parts:

  • grouped: Returns an array of Iterables of size n, comprising all the elements of the provided Iterable.
  • sameElements: Returns true if the two Iterables contain the same elements in the same order.
  • sliding: Returns an array of Iterables, being the result of grouping chunks of size n while traversing through a sliding window of size windowSize.
  • sliding: Returns an array of Iterables, being the result of grouping chunks of size n while traversing through a sliding window of size 1.
  • zip: Returns an array of tuples, each containing the corresponding elements from the provided Iterables.
  • zipAll: Returns an array of tuples, each containing the corresponding elements from the provided Iterables. If the two sources aren't the same size, zipAll will fill the gaps by using the provided default items (if any).
  • zipWithIndex: Returns an array of tuples, each containing an element from the provided Iterable and its index.

DATA TYPES

ArrayT

An immutable, traversable and typed Array.

import SecondBridge

let anArray : ArrayT<Int> = [1, 2, 3, 4, 5, 6] 
let list = anArray.toList()
anArray.isEmpty()  // False
anArray.size()  // 6
anArray.drop(4)  // [5,6]
anArray.filterNot({ $0 == 1 })  // [2, 3, 4, 5, 6]

BinarySearchTree

An immutable binary search tree.

import SecondBridge

let tree = BinarySearchTree.Node(2, left: BinarySearchTree.Empty, right: BinarySearchTree.Empty) // [. 2 .]
let tree2 = tree.add(4).add(8).add(5).add(1).add(3).add(7).add(10)  // [[. 1 .] 2 [[. 3 .] 4 [[. 5 [. 7 .]] 8 [. 10 .]]]]
let tree3 = tree.remove(5)  // [[. 1 .] 2 [[. 3 .] 4 [[. 7 .] 8 [. 10 .]]]]
tree2.count() // 8
tree2.search(1)  // true
tree2.inOrderTraversal() // [1, 2, 3, 4, 5, 7, 8, 10]

ListT

An immutable, traversable and typed List.

import SecondBridge

let a : ListT<Int> = [1,2,3,4]
a.head()  // 1
a.tail()  // [2,3,4]
a.length()  // 4
a.filter({$0 % 3 == 0})  //  [1,2,4]

Interactive Playground about Lists

Map

An immutable, unordered, traversable and iterable collection containing pairs of keys and values. Values are typed, but Second Bridge supports several types of keys within one Map (i.e.: Int, Float and String) inside a container called HashableAny.

import SecondBridge

let map : Map<Int> = ["a" : 1, 2 : 2, 4.5 : 3]
let map2 = map + ["c" : 4]			// map2 = ["a" : 1, 2 : 2, 4.5 : 3, "c" : 4]
let map3 = map2 + ("d", 5)			// map3 = ["a" : 1, 2 : 2, 4.5 : 3, "c" : 4, "d" : 5]
let map4 = map3 + [("foo", 7), ("bar", 8)]	// map4 = ["a" : 1, 2 : 2, 4.5 : 3, "c" : 4, "d" : 5, "foo" : 7,  "bar" : 8]

let map5 = map4 - "d"						// map5 = ["a" : 1, 2 : 2, 4.5 : 3, "c" : 4, "foo" : 7,  "bar" : 8]
let map 6 = map5 -- ["foo", "bar"]			// map6 = ["a" : 1, 2 : 2, 4.5 : 3, "c" : 4]

let filteredMap = map6.filter({ (value) -> Bool in (value as Int) < 3})  // ("a" : 1, 2 : 2)
let reducedResult = map6.reduceByValue(0, combine: +)   					// 10
let values = map6.values 												// [1, 2, 3, 4]

Interactive Playground about Maps

Stack

An immutable, traversable, iterable and typed LIFO stack.

import SecondBridge

let stack = Stack<Int>()
let stack2 = stack.push(1).push(2).push(3)    // top -> 3, 2, 1 <- bottom

let topStack = stack2.top() // 3
let popStack = stack2.pop()	// (3, Stack[2, 1])

Vector

An immutable, traversable, iterable and typed Persistent Bit-partitioned Vector Trie, based on Haskell and Scala's Vector implementations.

import SecondBridge

let vector = Vector<Int>()		// Empty vector
vector = vector.append(1) 		// [1]
vector = vector + 2				// [1, 2]
vector += 3						// [1, 2, 3]
let value = vector[1]			// 2
vector = vector.pop()			// [1, 2]

Interactive Playground about Vectors

FUNCTIONS

Partial Function

A partial function are those whose execution is restricted to a certain set of values, defined by the method isDefinedAt. This allows developers to set certain conditions to their functions. An easy to understand example is a divider function, whose execution is restricted to dividers equal to zero. As with Scala's partial functions, you are allowed to execute them even by using their restricted set of parameters. But you have access to the isDefinedAt function that tells you if it's safe to call.

Second Bridge defines several custom operators that makes creating partial functions really easy. Just by joining two closures with the |-> operator, we create a partial function (the first closure must return a Bool, thus defining the conditions under which this function works).

One important aspect of partial functions is that by combining them you can create meaningful pieces of code that define algorithms. i.e.: you can create a combined function that performs an operation or another, depending on the received parameter. You have access to other custom operators like |||> (OR), >>> (AND THEN), and (function definition). One quick example:

import SecondBridge

let doubleEvens = { $0 % 2 == 0 } |-> { $0 * 2 }		// Multiply by 2 any even number
let tripleOdds = { $0 % 2 != 0 } |-> { $0 * 3 }			// Multiply by 3 any odd number
let addFive = (+5)									  // Regular function to add five to any number

let opOrElseOp = doubleEvens |||> tripleOdds		// If receiving an even, double it. If not, triple it.
let opOrElseAndThenOp = doubleEvens |||> tripleOdds >>> addFive	// If receiving an even, double it. If not, triple it. Then, add five to the result.

opOrElseOp.apply(3)			// 9
opOrElseOp.apply(4)			// 8
opOrElseAndThenOp.apply(3)	// 14
opOrElseAndThenOp.apply(4)	// 13

Partial functions also gives us the ability to perform complex pattern matching sets, more powerful than Swift's standard switch block, by using our match function:

import SecondBridge
let matchTest = match({(item: Int) -> Bool in item == 0 } |-> {(Int) -> String in return "Zero"},
                              {(item: Int) -> Bool in item == 1 } |-> {(Int) -> String in return "One"},
                              {(item: Int) -> Bool in item == 2 } |-> {(Int) -> String in return "Two"},
                              {(item: Int) -> Bool in item > 2 } |-> {(Int) -> String in return "Moar!"})
                              
matchTest.apply(0)			// "Zero"
matchTest.apply(1)			// "One"
matchTest.apply(1000)		// "Moar!"

UTILS

Try

Try is a monad that encapsulates an operation that can fail and throw an exception. As you may be aware, Swift now supports do-try-catch blocks to handle operations that can fail. Try can wrap throwable functions (those marked with the throws keyword) to handle these failures for you. The result of the wrapped operation is stored in a TryMatcher.Success(x) if it's a valid result x, or TryMatcher.Failure(ex) if the operation has thrown an ex exception.

import SecondBridge

// Throwable function
func convertStringToInt(s: String) throws -> Int {
     if let parsedInt = Int(s) {
         return parsedInt
     } else {
         throw ParseError.InvalidString
     }
}

let tryParseCorrectString = Try<Int>(try self.convertStringToInt("47"))
tryParseCorrectString.isFailure()					// false
tryParseCorrectString.isSuccess()					// true

// You can get the result of the operations through pattern matching...
switch tryParseCorrectString.matchResult {
        case .Success(let value): print("Result is \(value)")
        case .Failure(let ex): print("There has been an exception!")
}

// ...or using convenience functions like getOrElse
let value = tryParseCorrectString.getOrElse(0)		// 47

let tryParseIncorrectString = Try<Int>(try self.convertStringToInt("47 Degrees"))
tryParseCorrectString.isFailure()							// true
tryParseCorrectString.isSuccess()							// false
let invalidValue = tryParseIncorrectString.getOrElse(666)   // 666

// You can apply several Higher-Order Functions to Try instances
// to apply functions to the encapsulated values:
let f = { (n: Int) -> Int in n + 10 }
let mapCorrectResult = tryParseCorrectString.map(f).getOrElse(666)	// 57

let filterCorrectResult = 
	tryParseCorrectString.filter({ $0 != 47 })		// .Failure(exception)

func tryHalf(n: Int) -> Try<Int> {
	// Returns a Try containing a function that divides any Int by two
	// ...
}
let flatmapCorrectResultAgainstOKFunction = tryParseCorrectString.flatMap(tryHalf)
flatmapCorrectResultAgainstOKFunction.isSuccess()	// true
flatmapCorrectResultAgainstOKFunction.getOrElse(1)	// 23

// You can also use `recover` and `recoverWith` to chain a set of
// Partial Functions that can handle failures in your `Try`s:
let recoverResult = tryParseIncorrectString.recover({
            (e: ErrorType) -> Bool in
                return true
            } |-> {(e: ErrorType) -> (Int) in return 0})

recoverResult.isSuccess()								// true
let recoverResultGet = recoverResult.getOrElse(1)		// 0

System Requirements

Second Bridge supports iOS 8.0+ and Swift 2.0.

Contribute

We've tried to pack Second Bridge with many useful features from the Scala language. But considering all the work done there, we have a lot of ground to cover yet. That's why 47 Degrees wants YOU! Second Bridge is completely open-source, and we're really looking forward to see what you can come up with! So if you're interested in Second Bridge and think that you've come up with a great feature to add... don't hesitate and send us a PR! We'll review and incorporate it to our code-base as soon as possible.

License

Copyright (C) 2015 47 Degrees, LLC http://47deg.com [email protected]

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

second-bridge's People

Contributors

anamariamv avatar juancazalla avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

second-bridge's Issues

Thunks syntax/custom operator

In Scala, thunks are represented by the : => syntax, encapsulating a function that takes no parameters and return a value; to achieve lazy input parameters in functions. It would be nice to use custom operators to create thunks easily, so we can have parameters by name in Swift too.

Implementation of Promise

Related to the implementation of Futures (issue 47), it'd be nice to have an implementation for Promises.

State - Swiftz to Second Bridge

Swiftz has an implementation of the State monad. Take a look at it, and see if it's worthy to add on it (i.e.: Scala has a different syntax, how it behaves with what we have in Second Bridge, etc...)

Add Foldable support to Try

We should take a look on implementing Swiftz's Foldable protocol to Try, or if it doesn't make sense, try to implement it on our own based on a suggestion in this pull request:

#48

myTry fold (fail = (ex) -> T , succ = (result) -> T)

Demo apps showcasing FP in Swift

Playgrounds examples are nice, but it would be great to have some examples of using Functional Programming concepts, immutable data types, combinators, etc... in some real small apps. Maybe have them inside the project as separate folders or something.

Free Monads & Trampolines

For a correct implementation of Futures (based on Scalaz's ones), we need an implementation of Free Monads and Trampolines based on them.

Fix Vector issues with Swift 2.1

Apple has changed the way ArraySlices indices work, so Vector now is having a bad time... Need to take a look at how the slices are being built and figure out a way to handle this new way of handling indices.

Also adding some additional tests to check Vector's data integrity would be nice.

Streams - Swiftz to Second Bridge

Take a look at Swiftz's new implementation of Streams and see how we can bring it to Second Bridge data-types and the Traversable/Iterable protocols.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.