diff --git a/index.js b/index.js index ea85cd6b..b8501a57 100644 --- a/index.js +++ b/index.js @@ -1698,7 +1698,7 @@ //. > S.encaseEither(S.I, JSON.parse, '[') //. Left(new SyntaxError('Unexpected end of input')) //. - //. > S.encaseEither(R.prop('message'), JSON.parse, '[') + //. > S.encaseEither(S.prop('message'), JSON.parse, '[') //. Left('Unexpected end of input') //. ``` S.encaseEither = @@ -2369,6 +2369,29 @@ //. ### Object + //# prop :: Accessible a => String -> a -> b + //. + //. Takes a property name and returns the value of that property from the + //. specified object; otherwise throws an error. + //. + //. ```javascript + //. > S.prop('a', {a: 1, b: 2}) + //. 1 + //. ``` + S.prop = + def('prop', + {a: [Accessible]}, + [$.String, a, b], + function(key, obj) { + if (key in obj) { + return obj[key]; + } else { + throw new TypeError('‘prop’ expected object to have a property ' + + 'named ‘' + key + '’; ' + R.toString(obj) + + ' does not'); + } + }); + //# get :: Accessible a => TypeRep b -> String -> a -> Maybe b //. //. Takes a [type representative](#type-representatives), a property diff --git a/test/index.js b/test/index.js index 04082a2e..b91e62b0 100644 --- a/test/index.js +++ b/test/index.js @@ -2089,7 +2089,7 @@ describe('either', function() { }); it('applies the first argument to the Error', function() { - eq(S.encaseEither(R.prop('message'), factorial, -1), + eq(S.encaseEither(S.prop('message'), factorial, -1), S.Left('Cannot determine factorial of negative number')); }); @@ -2135,7 +2135,7 @@ describe('either', function() { }); it('applies the first argument to the Error', function() { - eq(S.encaseEither2(R.prop('message'), rem, 42, 0), + eq(S.encaseEither2(S.prop('message'), rem, 42, 0), S.Left('Cannot divide by zero')); }); @@ -2182,7 +2182,7 @@ describe('either', function() { }); it('applies the first argument to the Error', function() { - eq(S.encaseEither3(R.prop('message'), area, 2, 2, 5), + eq(S.encaseEither3(S.prop('message'), area, 2, 2, 5), S.Left('Impossible triangle')); }); @@ -3330,6 +3330,48 @@ describe('list', function() { describe('object', function() { + describe('prop', function() { + + it('is a binary function', function() { + eq(typeof S.prop, 'function'); + eq(S.prop.length, 2); + }); + + it('type checks its arguments', function() { + assert.throws(function() { S.prop(1); }, + errorEq(TypeError, + '‘prop’ expected a value of type String ' + + 'as its first argument; received 1')); + + assert.throws(function() { S.prop('a', null); }, + errorEq(TypeError, + '‘prop’ requires ‘a’ to implement Accessible; ' + + 'Null does not')); + }); + + it('throws when the key is not present', function() { + assert.throws(function() { S.prop('c', {a: 0, b: 1}); }, + errorEq(TypeError, + '‘prop’ expected object to have a property named ' + + '‘c’; {"a": 0, "b": 1} does not')); + }); + + it('retrieves the specified key', function() { + eq(S.prop('a', {a: 0, b: 1}), 0); + eq(S.prop('0', [1, 2, 3]), 1); + + var proto = {x: 1, y: 2}; + var obj = Object.create(proto); + eq(S.prop('x', obj), 1); + }); + + it('is curried', function() { + eq(S.prop('a').length, 1); + eq(S.prop('a')({a: 0, b: 1}), 0); + }); + + }); + describe('get', function() { it('is a ternary function', function() {