File size: 4,758 Bytes
5fae594
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158

describe("functional", function() {

   describe("varargs", function() {
   
      describe('with fixed arguments', function() {
            
            var received1, received2, receivedRest;
         
            function f(a1, a2, array){
               received1 = a1;
               received2 = a2;
               receivedRest = array;
            }
         
            var varargsSpy = varArgs(f);
            
            varargsSpy('a', 'b', 'c', 'd', 'e', 'f');
            
            it( 'passes through the first arguments as-is' , function() {
            
               expect(received1).toBe('a');
               expect(received2).toBe('b');            
            });
            
            it( 'passes through the rest as an array', function() {
            
               expect(receivedRest).toEqual(['c', 'd', 'e', 'f']);
            });
      });
         
      it("works with no fixed arguments", function() {
          
         var received1 = 'not yet set';
      
         function f(r1){
            received1 = r1;
         }
      
         var varargsSpy = varArgs(f);
         
         varargsSpy('a', 'b', 'c', 'd', 'e', 'f');
         
         expect(received1).toEqual(['a', 'b', 'c', 'd', 'e', 'f']);
      });
         
              
      it('propagates the return value', function() {
               
         var varargsTestFn = varArgs(function(){ return 'expected' });
                  
         expect( varargsTestFn() ).toBe('expected');                   
      });   
   });
   
   
   describe('compose', function() {

      function dub(n){ return n*2 }
      function inc(n){ return n+1 }
      function half(n){ return div(n,2) }
      function div(a, b){ return a/b }
   
      it('executes composed functions right-to-left', function(){
         
         var composed = compose(dub, inc, half);  // composed(x) = dub(inc(half(x)))
         
         expect(composed(2)).toBe( 4 ); // if this gives 2.5 the order is wrong      
      });
      
      it('may have an outermost function taking more than one argument', function() {
   
         var composed = compose(inc, div);  // composed(x, y) = x/y +1
   
         expect( composed( 10, 2 ) ).toBe(6); // 10/2 +1 = 5+1 = 6     
      });      
      
      it('can compose head to take an item off a list and then use it', function() {
         var list = cons( {a:1}, emptyList );
   
         expect( compose(attr('a'), head)( list ) ).toBe(1);      
      });
      
      it('can compose one function to give just that function', function() {
   
         expect( compose(div)( 20, 5 ) ).toBe(4);      
      });      
      
      it('gives an identity function when making a composition of zero functions', function() {
         var id = compose();
         
         expect( id(2) ).toBe(2);      
      });


      describe('compose2', function() {
         // optimised version of compose that takes exactly two arguments

         it('executes composed functions right-to-left', function(){
            
            var composed = compose2(dub, inc);  // composed(x) = dub(inc(x))
            
            expect(composed(2)).toBe( 6 ); // if this gives 5 the order is wrong      
         });
         
         it('may have an outermost function taking more than one argument', function() {
      
            var composed = compose2(inc, div);  // composed(x, y) = x/y +1
      
            expect( composed( 10, 2 ) ).toBe(6); // 10/2 +1 = 5+1 = 6     
         });      
         
         it('can compose head to take an item off a list and then use it', function() {
            var list = cons( {a:1}, emptyList );
      
            expect( compose2(attr('a'), head)( list ) ).toBe(1);      
         });
         
         it('passes scope to both functions', function() {
      
            var expectedScope = {};
      
            function f(){
               expect(this).toBe(expectedScope);
            }
      
            var composed = compose2(f, f);  // composed(x, y) = x/y +1
      
            composed.apply(expectedScope);     
         });         
                                    
      })
       
   });
   
   
   
   describe('attr', function() {
      it('can get a value from an object at the named key', function() {
         var getA = attr('A');
      
         expect( getA({A:'B'}) ).toBe( 'B' );
      });
      
      it('can get the length of a string', function() {
         var getLength = attr('length');
      
         expect( getLength("hello") ).toBe( 5 );
      });      
      
      it('can get a numbered array element out', function() {
         var getLength = attr(0);
      
         expect( getLength(['a','b','c']) ).toBe( 'a' );
      });            
   })
   
});