fantasy land updates
This commit is contained in:
parent
4b40d2d3b5
commit
5eb008720a
3 changed files with 134 additions and 87 deletions
90
docs/prop.md
90
docs/prop.md
|
|
@ -2,18 +2,19 @@
|
||||||
|
|
||||||
- [API](#api)
|
- [API](#api)
|
||||||
- [Static members](#static-members)
|
- [Static members](#static-members)
|
||||||
- [prop.combine](#prop-combine)
|
- [prop.combine](#propcombine)
|
||||||
- [prop.reject](#prop-reject)
|
- [prop.reject](#propreject)
|
||||||
- [prop.merge](#prop-merge)
|
- [prop.merge](#propmerge)
|
||||||
- [prop.HALT](#prop-halt)
|
- [prop.HALT](#prophalt)
|
||||||
|
- [prop["fantasy-land/of"]](#propfantasylandof)
|
||||||
- [Instance members](#static-members)
|
- [Instance members](#static-members)
|
||||||
- [stream.run](#stream-run)
|
- [stream.run](#streamrun)
|
||||||
- [stream.end](#stream-end)
|
- [stream.end](#streamend)
|
||||||
- [stream.error](#stream-error)
|
- [stream.error](#streamerror)
|
||||||
- [stream.catch](#stream-catch)
|
- [stream.catch](#streamcatch)
|
||||||
- [stream.of](#stream-of)
|
- [stream["fantasy-land/of"]](#streamfantasylandof)
|
||||||
- [stream.map](#stream-map)
|
- [stream["fantasy-land/map"]](#streamfantasylandmap)
|
||||||
- [stream.ap](#stream-ap)
|
- [stream["fantasy-land/ap"]](#streamfantasylandap)
|
||||||
- [Basic usage](#basic-usage)
|
- [Basic usage](#basic-usage)
|
||||||
- [Streams as variables](#streams-as-variables)
|
- [Streams as variables](#streams-as-variables)
|
||||||
- [Bidirectional bindings](#bidirectional-bindings)
|
- [Bidirectional bindings](#bidirectional-bindings)
|
||||||
|
|
@ -103,6 +104,17 @@ Argument | Type | Required | Description
|
||||||
|
|
||||||
A special value that can be returned to stream callbacks to halt execution of downstreams
|
A special value that can be returned to stream callbacks to halt execution of downstreams
|
||||||
|
|
||||||
|
##### prop["fantasy-land/of"]
|
||||||
|
|
||||||
|
This method is functionally identical to `m.prop`. It exists to conform to [Fantasy Land's Applicative specification](https://github.com/fantasyland/fantasy-land). For more information, see the [What is Fantasy Land](#what-is-fantasy-land) section.
|
||||||
|
|
||||||
|
`stream = m.prop["fantasy-land/of"](value)`
|
||||||
|
|
||||||
|
Argument | Type | Required | Description
|
||||||
|
----------- | -------------------- | -------- | ---
|
||||||
|
`value` | `any` | No | If this argument is present, the value of the prop is set to it
|
||||||
|
**returns** | `Stream` | | Returns a stream
|
||||||
|
|
||||||
#### Instance members
|
#### Instance members
|
||||||
|
|
||||||
##### stream.run
|
##### stream.run
|
||||||
|
|
@ -145,26 +157,26 @@ Argument | Type | Required | Description
|
||||||
|
|
||||||
[How to read signatures](signatures.md)
|
[How to read signatures](signatures.md)
|
||||||
|
|
||||||
##### stream.of
|
##### stream["fantasy-land/of"]
|
||||||
|
|
||||||
This method is functionally identical to `m.prop`. It exists to conform to [Fantasy Land's Applicative specification](https://github.com/fantasyland/fantasy-land)
|
This method is functionally identical to `m.prop`. It exists to conform to [Fantasy Land's Applicative specification](https://github.com/fantasyland/fantasy-land). For more information, see the [What is Fantasy Land](#what-is-fantasy-land) section.
|
||||||
|
|
||||||
`stream = m.prop().of(value)`
|
`stream = m.prop()["fantasy-land/of"](value)`
|
||||||
|
|
||||||
Argument | Type | Required | Description
|
Argument | Type | Required | Description
|
||||||
----------- | -------------------- | -------- | ---
|
----------- | -------------------- | -------- | ---
|
||||||
`value` | `any` | No | If this argument is present, the value of the prop is set to it
|
`value` | `any` | No | If this argument is present, the value of the prop is set to it
|
||||||
**returns** | `Stream` | | Returns a stream
|
**returns** | `Stream` | | Returns a stream
|
||||||
|
|
||||||
##### stream.map
|
##### stream["fantasy-land/map"]
|
||||||
|
|
||||||
Creates a dependent stream whose value is set to the result of the callback function. See [chaining streams](#chaining-streams)
|
Creates a dependent stream whose value is set to the result of the callback function. See [chaining streams](#chaining-streams)
|
||||||
|
|
||||||
This method is almost functionally identical to [`stream.run()`](#stream-run), except that if the return value is a stream, the stream is not absorbed.
|
This method is almost functionally identical to [`stream.run()`](#stream-run), except that if the return value is a stream, the stream is not absorbed.
|
||||||
|
|
||||||
This method exists to conform to [Fantasy Land's Applicative specification](https://github.com/fantasyland/fantasy-land)
|
This method exists to conform to [Fantasy Land's Applicative specification](https://github.com/fantasyland/fantasy-land). For more information, see the [What is Fantasy Land](#what-is-fantasy-land) section.
|
||||||
|
|
||||||
`dependentStream = m.prop().map(callback)`
|
`dependentStream = m.prop()["fantasy-land/of"](callback)`
|
||||||
|
|
||||||
Argument | Type | Required | Description
|
Argument | Type | Required | Description
|
||||||
------------ | -------------------- | -------- | ---
|
------------ | -------------------- | -------- | ---
|
||||||
|
|
@ -173,11 +185,13 @@ Argument | Type | Required | Description
|
||||||
|
|
||||||
[How to read signatures](signatures.md)
|
[How to read signatures](signatures.md)
|
||||||
|
|
||||||
##### stream.ap
|
##### stream["fantasy-land/ap"]
|
||||||
|
|
||||||
The name of this method stands for `apply`. If a stream has a function as its value, calling `ap` will call the function with the value of the input stream as its argument, and it will return another stream whose value is the result of the function call. This method exists to conform to [Fantasy Land's Applicative specification](https://github.com/fantasyland/fantasy-land)
|
[FIXME: `ap` does not conform to Fantasy Land 2.0 spec - do not use]
|
||||||
|
|
||||||
`errorStream = m.prop().ap(value)`
|
The name of this method stands for `apply`. If a stream has a function as its value, calling `ap` will call the function with the value of the input stream as its argument, and it will return another stream whose value is the result of the function call. This method exists to conform to [Fantasy Land's Applicative specification](https://github.com/fantasyland/fantasy-land). For more information, see the [What is Fantasy Land](#what-is-fantasy-land) section.
|
||||||
|
|
||||||
|
`errorStream = m.prop()["fantasy-land/ap"](value)`
|
||||||
|
|
||||||
Argument | Type | Required | Description
|
Argument | Type | Required | Description
|
||||||
----------- | -------------------- | -------- | ---
|
----------- | -------------------- | -------- | ---
|
||||||
|
|
@ -280,7 +294,7 @@ var RobustExample = {
|
||||||
},
|
},
|
||||||
view: function(vnode) {
|
view: function(vnode) {
|
||||||
return [
|
return [
|
||||||
vnode.state.items() ? vnode.state.items().map(function(item) {
|
vnode.state.items() ? vnode.state.items().run(function(item) {
|
||||||
return m("div", item.name)
|
return m("div", item.name)
|
||||||
}) : m(".loading-icon"),
|
}) : m(".loading-icon"),
|
||||||
vnode.state.error(),
|
vnode.state.error(),
|
||||||
|
|
@ -474,7 +488,7 @@ var mapped = m.prop.reject(new Error("error")).catch(function(e) {
|
||||||
console.log(mapped()) // logs 2
|
console.log(mapped()) // logs 2
|
||||||
```
|
```
|
||||||
|
|
||||||
Stream absorption does not occur in fantasy-land methods (i.e. `.map()`, `.ap()`, `.of()`)
|
Stream absorption does not occur in fantasy-land methods (i.e. `["fantasy-land/map"]()`, `["fantasy-land/ap"]()`, `["fantasy-land/of"]()`)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -698,3 +712,35 @@ console.log("test " + stream) // logs "test 123"
|
||||||
Unlike libraries like Knockout, Mithril streams do not trigger re-rendering of templates. Redrawing happens in response to event handlers defined in Mithril component views, route changes, or after [`m.request`](request.md) calls resolve.
|
Unlike libraries like Knockout, Mithril streams do not trigger re-rendering of templates. Redrawing happens in response to event handlers defined in Mithril component views, route changes, or after [`m.request`](request.md) calls resolve.
|
||||||
|
|
||||||
If redrawing is desired in response to other asynchronous events (e.g. `setTimeout`/`setInterval`, websocket subscription, 3rd party library event handler, etc), you should manually call [`m.redraw()`](redraw.md)
|
If redrawing is desired in response to other asynchronous events (e.g. `setTimeout`/`setInterval`, websocket subscription, 3rd party library event handler, etc), you should manually call [`m.redraw()`](redraw.md)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### What is Fantasy Land
|
||||||
|
|
||||||
|
[Fantasy Land](https://github.com/fantasyland/fantasy-land) specifies interoperability of common algebraic structures. In plain english, that means that libraries that conform to Fantasy Land specs can be used to write generic functional style code that works regardless of how these libraries implement the constructs.
|
||||||
|
|
||||||
|
For example, say we want to create a generic function called `plusOne`. The naive implementation would look like this:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
function plusOne(a) {
|
||||||
|
return a + 1
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The problem with this implementation is that it can only be used with a number. However it's possible that whatever logic produces a value for `a` could also produce an error state (wrapped in a Maybe or an Either from a library like [Sanctuary](https://github.com/sanctuary-js/sanctuary) or [Ramda-Fantasy](https://github.com/ramda/ramda-fantasy)), or it could be a Mithril stream, or a [flyd](https://github.com/paldepind/flyd) stream, etc. Ideally, we wouldn't want to write a similar version of the same function for every possible type that `a` could have and we wouldn't want to be writing wrapping/unwrapping/error handling code repeatedly.
|
||||||
|
|
||||||
|
This is where Fantasy Land can help. Let's rewrite that function in terms of a Fantasy Land algebra:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var fl = require("fantasy-land")
|
||||||
|
|
||||||
|
function plusOne(a) {
|
||||||
|
return a[fl.map](function(value) {return value + 1})
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Now this method works with any Fantasy Land compliant [Functor](https://github.com/fantasyland/fantasy-land#functor), such as [`R.Maybe`](https://github.com/ramda/ramda-fantasy/blob/master/docs/Maybe.md), [`S.Either`](https://github.com/sanctuary-js/sanctuary#either-type), `m.prop`, etc.
|
||||||
|
|
||||||
|
This example may seem convoluted, but it's a trade-off in complexity: the naive `plusOne` implementation makes sense if you have a simple system and only ever increment numbers, but the Fantasy Land implementation becomes more powerful if you have a large system with many wrapper abstractions and reused algorithms.
|
||||||
|
|
||||||
|
When deciding whether you should adopt Fantasy Land, you should consider your team's familiarity with functional programming, and be realistic regarding the level of discipline that your team can commit to maintaining code quality (vs the pressure of writing new features and meeting deadlines). Functional style programming heavily depends on compiling, curating and mastering a large set of small, precisely defined functions, and therefore it's not suitable for teams who do not have solid documentation practices, and/or lack experience in functional oriented languages.
|
||||||
|
|
@ -16,7 +16,7 @@ module.exports = function(log) {
|
||||||
function initStream(stream) {
|
function initStream(stream) {
|
||||||
stream.constructor = createStream
|
stream.constructor = createStream
|
||||||
stream._state = {id: guid++, value: undefined, error: undefined, state: 0, derive: undefined, recover: undefined, deps: {}, parents: [], errorStream: undefined, endStream: undefined}
|
stream._state = {id: guid++, value: undefined, error: undefined, state: 0, derive: undefined, recover: undefined, deps: {}, parents: [], errorStream: undefined, endStream: undefined}
|
||||||
stream.map = map, stream.ap = ap, stream.of = createStream
|
stream["fantasy-land/map"] = map, stream["fantasy-land/ap"] = ap, stream["fantasy-land/of"] = createStream
|
||||||
stream.valueOf = valueOf, stream.toJSON = toJSON, stream.toString = valueOf
|
stream.valueOf = valueOf, stream.toJSON = toJSON, stream.toString = valueOf
|
||||||
stream.run = run, stream.catch = doCatch
|
stream.run = run, stream.catch = doCatch
|
||||||
|
|
||||||
|
|
@ -195,6 +195,7 @@ module.exports = function(log) {
|
||||||
return streams.map(function(s) {return s()})
|
return streams.map(function(s) {return s()})
|
||||||
}, streams)
|
}, streams)
|
||||||
}
|
}
|
||||||
|
createStream["fantasy-land/of"] = createStream
|
||||||
createStream.merge = merge
|
createStream.merge = merge
|
||||||
createStream.combine = combine
|
createStream.combine = combine
|
||||||
createStream.reject = reject
|
createStream.reject = reject
|
||||||
|
|
|
||||||
|
|
@ -164,7 +164,7 @@ o.spec("stream", function() {
|
||||||
var b = Stream.combine(function(a) {
|
var b = Stream.combine(function(a) {
|
||||||
return Stream.HALT
|
return Stream.HALT
|
||||||
}, [a])
|
}, [a])
|
||||||
.map(function() {
|
["fantasy-land/map"](function() {
|
||||||
count++
|
count++
|
||||||
return 1
|
return 1
|
||||||
})
|
})
|
||||||
|
|
@ -316,12 +316,12 @@ o.spec("stream", function() {
|
||||||
o("thrown error propagates downstream", function() {
|
o("thrown error propagates downstream", function() {
|
||||||
var count = 0
|
var count = 0
|
||||||
var stream = Stream(1)
|
var stream = Stream(1)
|
||||||
.map(function() {throw new Error("error")})
|
["fantasy-land/map"](function() {throw new Error("error")})
|
||||||
.map(function(value) {
|
["fantasy-land/map"](function(value) {
|
||||||
count++
|
count++
|
||||||
return value * 2
|
return value * 2
|
||||||
})
|
})
|
||||||
.map(function(value) {
|
["fantasy-land/map"](function(value) {
|
||||||
count++
|
count++
|
||||||
return value * 3
|
return value * 3
|
||||||
})
|
})
|
||||||
|
|
@ -333,11 +333,11 @@ o.spec("stream", function() {
|
||||||
o("set error propagates downstream", function() {
|
o("set error propagates downstream", function() {
|
||||||
var count = 0
|
var count = 0
|
||||||
var stream = Stream()
|
var stream = Stream()
|
||||||
var mapped = stream.map(function(value) {
|
var mapped = stream["fantasy-land/map"](function(value) {
|
||||||
count++
|
count++
|
||||||
return value * 2
|
return value * 2
|
||||||
})
|
})
|
||||||
.map(function(value) {
|
["fantasy-land/map"](function(value) {
|
||||||
count++
|
count++
|
||||||
return value * 3
|
return value * 3
|
||||||
})
|
})
|
||||||
|
|
@ -347,9 +347,9 @@ o.spec("stream", function() {
|
||||||
o(mapped.error().message).equals("error")
|
o(mapped.error().message).equals("error")
|
||||||
o(count).equals(0)
|
o(count).equals(0)
|
||||||
})
|
})
|
||||||
o("error.map works", function() {
|
o("error["fantasy-land/map"] works", function() {
|
||||||
var stream = Stream(1)
|
var stream = Stream(1)
|
||||||
var mappedFromError = stream.error.map(function(value) {
|
var mappedFromError = stream.error["fantasy-land/map"](function(value) {
|
||||||
if (value) return "from" + value.message
|
if (value) return "from" + value.message
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -359,12 +359,12 @@ o.spec("stream", function() {
|
||||||
|
|
||||||
o(mappedFromError()).equals("fromerror")
|
o(mappedFromError()).equals("fromerror")
|
||||||
})
|
})
|
||||||
o("error from error.map propagates", function() {
|
o("error from error["fantasy-land/map"] propagates", function() {
|
||||||
var stream = Stream(1)
|
var stream = Stream(1)
|
||||||
var mappedFromError = stream.error.map(function(value) {
|
var mappedFromError = stream.error["fantasy-land/map"](function(value) {
|
||||||
return "from" + value.message
|
return "from" + value.message
|
||||||
})
|
})
|
||||||
.map(function(value) {
|
["fantasy-land/map"](function(value) {
|
||||||
return "a" + value
|
return "a" + value
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -374,14 +374,14 @@ o.spec("stream", function() {
|
||||||
|
|
||||||
o(mappedFromError()).equals("afromerror")
|
o(mappedFromError()).equals("afromerror")
|
||||||
})
|
})
|
||||||
o("error thrown from error.map propagates downstream", function() {
|
o("error thrown from error["fantasy-land/map"] propagates downstream", function() {
|
||||||
var count = 0
|
var count = 0
|
||||||
var stream = Stream(1)
|
var stream = Stream(1)
|
||||||
var mappedFromError = stream.error.map(function(value) {
|
var mappedFromError = stream.error["fantasy-land/map"](function(value) {
|
||||||
throw new Error("b")
|
throw new Error("b")
|
||||||
})
|
})
|
||||||
|
|
||||||
var downstream = mappedFromError.map(function() {
|
var downstream = mappedFromError["fantasy-land/map"](function() {
|
||||||
count++
|
count++
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -397,10 +397,10 @@ o.spec("stream", function() {
|
||||||
})
|
})
|
||||||
o("error can halt", function() {
|
o("error can halt", function() {
|
||||||
var count = 0
|
var count = 0
|
||||||
var stream = Stream.reject(1).error.map(function() {
|
var stream = Stream.reject(1).error["fantasy-land/map"](function() {
|
||||||
return Stream.HALT
|
return Stream.HALT
|
||||||
})
|
})
|
||||||
.map(function() {
|
["fantasy-land/map"](function() {
|
||||||
count++
|
count++
|
||||||
return 1
|
return 1
|
||||||
})
|
})
|
||||||
|
|
@ -408,9 +408,9 @@ o.spec("stream", function() {
|
||||||
o(stream()).equals(undefined)
|
o(stream()).equals(undefined)
|
||||||
o(count).equals(0)
|
o(count).equals(0)
|
||||||
})
|
})
|
||||||
o("error.map can return streams", function() {
|
o("error["fantasy-land/map"] can return streams", function() {
|
||||||
var stream = Stream.reject(new Error("error"))
|
var stream = Stream.reject(new Error("error"))
|
||||||
var error = stream.error.map(function(value) {
|
var error = stream.error["fantasy-land/map"](function(value) {
|
||||||
return Stream(1)
|
return Stream(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -436,11 +436,11 @@ o.spec("stream", function() {
|
||||||
o("rejected propagates downstream", function() {
|
o("rejected propagates downstream", function() {
|
||||||
var count = 0
|
var count = 0
|
||||||
var stream = Stream.reject(new Error("error"))
|
var stream = Stream.reject(new Error("error"))
|
||||||
.map(function(value) {
|
["fantasy-land/map"](function(value) {
|
||||||
count++
|
count++
|
||||||
return value * 2
|
return value * 2
|
||||||
})
|
})
|
||||||
.map(function(value) {
|
["fantasy-land/map"](function(value) {
|
||||||
count++
|
count++
|
||||||
return value * 3
|
return value * 3
|
||||||
})
|
})
|
||||||
|
|
@ -450,7 +450,7 @@ o.spec("stream", function() {
|
||||||
})
|
})
|
||||||
o("rejected removes error on value", function() {
|
o("rejected removes error on value", function() {
|
||||||
var stream = Stream.reject(new Error("error"))
|
var stream = Stream.reject(new Error("error"))
|
||||||
var doubled = stream.map(function(value) {
|
var doubled = stream["fantasy-land/map"](function(value) {
|
||||||
return value * 2
|
return value * 2
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -520,7 +520,7 @@ o.spec("stream", function() {
|
||||||
var count = 0
|
var count = 0
|
||||||
var stream = Stream(undefined)
|
var stream = Stream(undefined)
|
||||||
var errored = stream.run(function(value) {throw new Error("error")})
|
var errored = stream.run(function(value) {throw new Error("error")})
|
||||||
var mapped = errored.map(function(value) {
|
var mapped = errored["fantasy-land/map"](function(value) {
|
||||||
count++
|
count++
|
||||||
return value
|
return value
|
||||||
})
|
})
|
||||||
|
|
@ -536,7 +536,7 @@ o.spec("stream", function() {
|
||||||
var stream = Stream(undefined)
|
var stream = Stream(undefined)
|
||||||
var absorbed = Stream()
|
var absorbed = Stream()
|
||||||
var absorber = stream.run(function(value) {return absorbed})
|
var absorber = stream.run(function(value) {return absorbed})
|
||||||
var mapped = absorber.map(function(value) {
|
var mapped = absorber["fantasy-land/map"](function(value) {
|
||||||
count++
|
count++
|
||||||
return value
|
return value
|
||||||
})
|
})
|
||||||
|
|
@ -596,7 +596,7 @@ o.spec("stream", function() {
|
||||||
var absorbed = Stream()
|
var absorbed = Stream()
|
||||||
var mapped = stream.run(function(value) {return absorbed})
|
var mapped = stream.run(function(value) {return absorbed})
|
||||||
|
|
||||||
mapped.map(function (value) {
|
mapped["fantasy-land/map"](function (value) {
|
||||||
count += 1
|
count += 1
|
||||||
|
|
||||||
o(value).equals(123)
|
o(value).equals(123)
|
||||||
|
|
@ -651,7 +651,7 @@ o.spec("stream", function() {
|
||||||
o("throwing from absorbed propagates", function() {
|
o("throwing from absorbed propagates", function() {
|
||||||
var stream = Stream(undefined)
|
var stream = Stream(undefined)
|
||||||
var absorbedParent = Stream()
|
var absorbedParent = Stream()
|
||||||
var absorbed = absorbedParent.map(function() {throw new Error("error")})
|
var absorbed = absorbedParent["fantasy-land/map"](function() {throw new Error("error")})
|
||||||
var mapped = stream.run(function(value) {return absorbed})
|
var mapped = stream.run(function(value) {return absorbed})
|
||||||
|
|
||||||
o(mapped()).equals(undefined)
|
o(mapped()).equals(undefined)
|
||||||
|
|
@ -670,7 +670,7 @@ o.spec("stream", function() {
|
||||||
count++
|
count++
|
||||||
return "no" + e.message
|
return "no" + e.message
|
||||||
})
|
})
|
||||||
.map(function(value) {
|
["fantasy-land/map"](function(value) {
|
||||||
return value + "mapped"
|
return value + "mapped"
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -684,7 +684,7 @@ o.spec("stream", function() {
|
||||||
count++
|
count++
|
||||||
return "no" + e.message
|
return "no" + e.message
|
||||||
})
|
})
|
||||||
.map(function(value) {
|
["fantasy-land/map"](function(value) {
|
||||||
return value + "mapped"
|
return value + "mapped"
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -695,11 +695,11 @@ o.spec("stream", function() {
|
||||||
o("catch is not called if no error", function() {
|
o("catch is not called if no error", function() {
|
||||||
var count = 0
|
var count = 0
|
||||||
var stream = Stream()
|
var stream = Stream()
|
||||||
var handled = stream.map(function(value) {return value + value}).catch(function(e) {
|
var handled = stream["fantasy-land/map"](function(value) {return value + value}).catch(function(e) {
|
||||||
count++
|
count++
|
||||||
return "no" + e.message
|
return "no" + e.message
|
||||||
})
|
})
|
||||||
.map(function(value) {
|
["fantasy-land/map"](function(value) {
|
||||||
return value + "mapped"
|
return value + "mapped"
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -711,11 +711,11 @@ o.spec("stream", function() {
|
||||||
})
|
})
|
||||||
o("catch is not called if no error with default value", function() {
|
o("catch is not called if no error with default value", function() {
|
||||||
var count = 0
|
var count = 0
|
||||||
var stream = Stream("a").map(function(value) {return value + value}).catch(function(e) {
|
var stream = Stream("a")["fantasy-land/map"](function(value) {return value + value}).catch(function(e) {
|
||||||
count++
|
count++
|
||||||
return "no" + e.message
|
return "no" + e.message
|
||||||
})
|
})
|
||||||
.map(function(value) {
|
["fantasy-land/map"](function(value) {
|
||||||
return value + "mapped"
|
return value + "mapped"
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -727,7 +727,7 @@ o.spec("stream", function() {
|
||||||
var stream = Stream.reject(new Error("a")).catch(function(e) {
|
var stream = Stream.reject(new Error("a")).catch(function(e) {
|
||||||
throw new Error("b")
|
throw new Error("b")
|
||||||
})
|
})
|
||||||
var mapped = stream.map(function(value) {return value + "ok"})
|
var mapped = stream["fantasy-land/map"](function(value) {return value + "ok"})
|
||||||
|
|
||||||
o(stream()).equals(undefined)
|
o(stream()).equals(undefined)
|
||||||
o(stream.error().message).equals("b")
|
o(stream.error().message).equals("b")
|
||||||
|
|
@ -735,7 +735,7 @@ o.spec("stream", function() {
|
||||||
o(mapped.error().message).equals("b")
|
o(mapped.error().message).equals("b")
|
||||||
})
|
})
|
||||||
o("catch can return undefined", function() {
|
o("catch can return undefined", function() {
|
||||||
var stream = Stream.reject(new Error("b")).catch(function(e) {}).map(function(value) {return String(value)})
|
var stream = Stream.reject(new Error("b")).catch(function(e) {})["fantasy-land/map"](function(value) {return String(value)})
|
||||||
|
|
||||||
o(stream()).equals("undefined")
|
o(stream()).equals("undefined")
|
||||||
o(stream.error()).equals(undefined)
|
o(stream.error()).equals(undefined)
|
||||||
|
|
@ -746,7 +746,7 @@ o.spec("stream", function() {
|
||||||
var mapped = Stream.reject(new Error("b")).catch(function(e) {
|
var mapped = Stream.reject(new Error("b")).catch(function(e) {
|
||||||
return stream
|
return stream
|
||||||
})
|
})
|
||||||
.map(function(value) {
|
["fantasy-land/map"](function(value) {
|
||||||
count++
|
count++
|
||||||
return String(value)
|
return String(value)
|
||||||
})
|
})
|
||||||
|
|
@ -759,7 +759,7 @@ o.spec("stream", function() {
|
||||||
var mapped = Stream.reject(new Error("b")).catch(function(e) {
|
var mapped = Stream.reject(new Error("b")).catch(function(e) {
|
||||||
return stream
|
return stream
|
||||||
})
|
})
|
||||||
.map(function(value) {return String(value)})
|
["fantasy-land/map"](function(value) {return String(value)})
|
||||||
|
|
||||||
o(mapped()).equals("1")
|
o(mapped()).equals("1")
|
||||||
})
|
})
|
||||||
|
|
@ -768,15 +768,15 @@ o.spec("stream", function() {
|
||||||
var mapped = Stream.reject(new Error("b")).catch(function(e) {
|
var mapped = Stream.reject(new Error("b")).catch(function(e) {
|
||||||
return stream
|
return stream
|
||||||
})
|
})
|
||||||
.map(function(value) {return String(value)})
|
["fantasy-land/map"](function(value) {return String(value)})
|
||||||
|
|
||||||
o(mapped()).equals(undefined)
|
o(mapped()).equals(undefined)
|
||||||
o(mapped.error().message).equals("a")*/
|
o(mapped.error().message).equals("a")*/
|
||||||
})
|
})
|
||||||
o("catch does not prevent sibling error propagation", function() {
|
o("catch does not prevent sibling error propagation", function() {
|
||||||
var a = Stream.reject(new Error("a"))
|
var a = Stream.reject(new Error("a"))
|
||||||
var b = a.map(function(value) {return value + "b"}).catch(function(e) {})
|
var b = a["fantasy-land/map"](function(value) {return value + "b"}).catch(function(e) {})
|
||||||
var c = a.map(function(value) {return value + "c"})
|
var c = a["fantasy-land/map"](function(value) {return value + "c"})
|
||||||
var d = Stream.combine(function(b, c) {return b() + c()}, [b, c])
|
var d = Stream.combine(function(b, c) {return b() + c()}, [b, c])
|
||||||
|
|
||||||
o(d()).equals(undefined)
|
o(d()).equals(undefined)
|
||||||
|
|
@ -784,14 +784,14 @@ o.spec("stream", function() {
|
||||||
})
|
})
|
||||||
o("catches wrapped rejected stream", function() {
|
o("catches wrapped rejected stream", function() {
|
||||||
var caught
|
var caught
|
||||||
var stream = Stream(1).map(function() {
|
var stream = Stream(1)["fantasy-land/map"](function() {
|
||||||
return Stream.reject(new Error("error"))
|
return Stream.reject(new Error("error"))
|
||||||
})
|
})
|
||||||
.catch(function(value) {
|
.catch(function(value) {
|
||||||
caught = value
|
caught = value
|
||||||
return "no" + value.message
|
return "no" + value.message
|
||||||
})
|
})
|
||||||
.map(function(value) {
|
["fantasy-land/map"](function(value) {
|
||||||
return value + "mapped"
|
return value + "mapped"
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -799,8 +799,8 @@ o.spec("stream", function() {
|
||||||
})
|
})
|
||||||
o("catches nested wrapped rejected stream", function() {
|
o("catches nested wrapped rejected stream", function() {
|
||||||
var caught
|
var caught
|
||||||
var stream = Stream(1).map(function() {
|
var stream = Stream(1)["fantasy-land/map"](function() {
|
||||||
return Stream(2).map(function() {
|
return Stream(2)["fantasy-land/map"](function() {
|
||||||
return Stream.reject(new Error("error"))
|
return Stream.reject(new Error("error"))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
@ -808,7 +808,7 @@ o.spec("stream", function() {
|
||||||
caught = value
|
caught = value
|
||||||
return "no" + value.message
|
return "no" + value.message
|
||||||
})
|
})
|
||||||
.map(function(value) {
|
["fantasy-land/map"](function(value) {
|
||||||
return value + "mapped"
|
return value + "mapped"
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -866,7 +866,7 @@ o.spec("stream", function() {
|
||||||
})
|
})
|
||||||
o.spec("uncaught exception reporting", function() {
|
o.spec("uncaught exception reporting", function() {
|
||||||
o("reports thrown errors", function(done) {
|
o("reports thrown errors", function(done) {
|
||||||
Stream(1).map(function() {throw new Error("error")})
|
Stream(1)["fantasy-land/map"](function() {throw new Error("error")})
|
||||||
|
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
o(spy.callCount).equals(1)
|
o(spy.callCount).equals(1)
|
||||||
|
|
@ -886,7 +886,7 @@ o.spec("stream", function() {
|
||||||
o.spec("map", function() {
|
o.spec("map", function() {
|
||||||
o("works", function() {
|
o("works", function() {
|
||||||
var stream = Stream()
|
var stream = Stream()
|
||||||
var doubled = stream.map(function(value) {return value * 2})
|
var doubled = stream["fantasy-land/map"](function(value) {return value * 2})
|
||||||
|
|
||||||
stream(3)
|
stream(3)
|
||||||
|
|
||||||
|
|
@ -894,13 +894,13 @@ o.spec("stream", function() {
|
||||||
})
|
})
|
||||||
o("works with default value", function() {
|
o("works with default value", function() {
|
||||||
var stream = Stream(3)
|
var stream = Stream(3)
|
||||||
var doubled = stream.map(function(value) {return value * 2})
|
var doubled = stream["fantasy-land/map"](function(value) {return value * 2})
|
||||||
|
|
||||||
o(doubled()).equals(6)
|
o(doubled()).equals(6)
|
||||||
})
|
})
|
||||||
o("works with undefined value", function() {
|
o("works with undefined value", function() {
|
||||||
var stream = Stream()
|
var stream = Stream()
|
||||||
var mapped = stream.map(function(value) {return String(value)})
|
var mapped = stream["fantasy-land/map"](function(value) {return String(value)})
|
||||||
|
|
||||||
stream(undefined)
|
stream(undefined)
|
||||||
|
|
||||||
|
|
@ -908,13 +908,13 @@ o.spec("stream", function() {
|
||||||
})
|
})
|
||||||
o("works with default undefined value", function() {
|
o("works with default undefined value", function() {
|
||||||
var stream = Stream(undefined)
|
var stream = Stream(undefined)
|
||||||
var mapped = stream.map(function(value) {return String(value)})
|
var mapped = stream["fantasy-land/map"](function(value) {return String(value)})
|
||||||
|
|
||||||
o(mapped()).equals("undefined")
|
o(mapped()).equals("undefined")
|
||||||
})
|
})
|
||||||
o("works with pending stream", function() {
|
o("works with pending stream", function() {
|
||||||
var stream = Stream(undefined)
|
var stream = Stream(undefined)
|
||||||
var mapped = stream.map(function(value) {return Stream()})
|
var mapped = stream["fantasy-land/map"](function(value) {return Stream()})
|
||||||
|
|
||||||
o(mapped()()).equals(undefined)
|
o(mapped()()).equals(undefined)
|
||||||
})
|
})
|
||||||
|
|
@ -923,7 +923,7 @@ o.spec("stream", function() {
|
||||||
o("works", function() {
|
o("works", function() {
|
||||||
var apply = Stream(function(value) {return value * 2})
|
var apply = Stream(function(value) {return value * 2})
|
||||||
var stream = Stream(3)
|
var stream = Stream(3)
|
||||||
var applied = apply.ap(stream)
|
var applied = apply["fantasy-land/ap"](stream)
|
||||||
|
|
||||||
o(applied()).equals(6)
|
o(applied()).equals(6)
|
||||||
|
|
||||||
|
|
@ -938,7 +938,7 @@ o.spec("stream", function() {
|
||||||
o("works with undefined value", function() {
|
o("works with undefined value", function() {
|
||||||
var apply = Stream(function(value) {return String(value)})
|
var apply = Stream(function(value) {return String(value)})
|
||||||
var stream = Stream(undefined)
|
var stream = Stream(undefined)
|
||||||
var applied = apply.ap(stream)
|
var applied = apply["fantasy-land/ap"](stream)
|
||||||
|
|
||||||
o(applied()).equals("undefined")
|
o(applied()).equals("undefined")
|
||||||
|
|
||||||
|
|
@ -951,7 +951,7 @@ o.spec("stream", function() {
|
||||||
o.spec("functor", function() {
|
o.spec("functor", function() {
|
||||||
o("identity", function() {
|
o("identity", function() {
|
||||||
var stream = Stream(3)
|
var stream = Stream(3)
|
||||||
var mapped = stream.map(function(value) {return value})
|
var mapped = stream["fantasy-land/map"](function(value) {return value})
|
||||||
|
|
||||||
o(stream()).equals(mapped())
|
o(stream()).equals(mapped())
|
||||||
})
|
})
|
||||||
|
|
@ -961,8 +961,8 @@ o.spec("stream", function() {
|
||||||
|
|
||||||
var stream = Stream(3)
|
var stream = Stream(3)
|
||||||
|
|
||||||
var mapped = stream.map(function(value) {return f(g(value))})
|
var mapped = stream["fantasy-land/map"](function(value) {return f(g(value))})
|
||||||
var composed = stream.map(g).map(f)
|
var composed = stream["fantasy-land/map"](g)["fantasy-land/map"](f)
|
||||||
|
|
||||||
o(mapped()).equals(18)
|
o(mapped()).equals(18)
|
||||||
o(mapped()).equals(composed())
|
o(mapped()).equals(composed())
|
||||||
|
|
@ -974,15 +974,15 @@ o.spec("stream", function() {
|
||||||
var u = Stream(function(value) {return value * 3})
|
var u = Stream(function(value) {return value * 3})
|
||||||
var v = Stream(5)
|
var v = Stream(5)
|
||||||
|
|
||||||
var mapped = a.map(function(f) {
|
var mapped = a["fantasy-land/map"](function(f) {
|
||||||
return function(g) {
|
return function(g) {
|
||||||
return function(x) {
|
return function(x) {
|
||||||
return f(g(x))
|
return f(g(x))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}).ap(u).ap(v)
|
})["fantasy-land/ap"](u)["fantasy-land/ap"](v)
|
||||||
|
|
||||||
var composed = a.ap(u.ap(v))
|
var composed = a["fantasy-land/ap"](u["fantasy-land/ap"](v))
|
||||||
|
|
||||||
o(mapped()).equals(30)
|
o(mapped()).equals(30)
|
||||||
o(mapped()).equals(composed())
|
o(mapped()).equals(composed())
|
||||||
|
|
@ -990,27 +990,27 @@ o.spec("stream", function() {
|
||||||
})
|
})
|
||||||
o.spec("applicative", function() {
|
o.spec("applicative", function() {
|
||||||
o("identity", function() {
|
o("identity", function() {
|
||||||
var a = Stream().of(function(value) {return value})
|
var a = Stream()["fantasy-land/of"](function(value) {return value})
|
||||||
var v = Stream(5)
|
var v = Stream(5)
|
||||||
|
|
||||||
o(a.ap(v)()).equals(5)
|
o(a["fantasy-land/ap"](v)()).equals(5)
|
||||||
o(a.ap(v)()).equals(v())
|
o(a["fantasy-land/ap"](v)()).equals(v())
|
||||||
})
|
})
|
||||||
o("homomorphism", function() {
|
o("homomorphism", function() {
|
||||||
var a = Stream(0)
|
var a = Stream(0)
|
||||||
var f = function(value) {return value * 2}
|
var f = function(value) {return value * 2}
|
||||||
var x = 3
|
var x = 3
|
||||||
|
|
||||||
o(a.of(f).ap(a.of(x))()).equals(6)
|
o(a["fantasy-land/of"](f)["fantasy-land/ap"](a["fantasy-land/of"](x))()).equals(6)
|
||||||
o(a.of(f).ap(a.of(x))()).equals(a.of(f(x))())
|
o(a["fantasy-land/of"](f)["fantasy-land/ap"](a["fantasy-land/of"](x))()).equals(a["fantasy-land/of"](f(x))())
|
||||||
})
|
})
|
||||||
o("interchange", function() {
|
o("interchange", function() {
|
||||||
var u = Stream(function(value) {return value * 2})
|
var u = Stream(function(value) {return value * 2})
|
||||||
var a = Stream()
|
var a = Stream()
|
||||||
var y = 3
|
var y = 3
|
||||||
|
|
||||||
o(u.ap(a.of(y))()).equals(6)
|
o(u["fantasy-land/ap"](a["fantasy-land/of"](y))()).equals(6)
|
||||||
o(u.ap(a.of(y))()).equals(a.of(function(f) {return f(y)}).ap(u)())
|
o(u["fantasy-land/ap"](a["fantasy-land/of"](y))()).equals(a["fantasy-land/of"](function(f) {return f(y)})["fantasy-land/ap"](u)())
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue