31 |
32 |
33 | <% for (var i=0, l=sources.length; i
34 | <% var source = sources[i]; %>
35 |
36 | <%= path.basename(source) %>
37 |
38 | <% } %>
39 |
40 |
41 |
42 |
43 | <% } %>
44 |
45 | <% if (!hasTitle) { %>
46 | -
47 |
48 |
<%= title %>
49 |
50 |
51 | <% } %>
52 | <% for (var i=0, l=sections.length; i
53 | <% var section = sections[i]; %>
54 | -
55 |
56 | <% heading = section.docsHtml.match(/^\s*<(h\d)>/) %>
57 |
60 | <%= section.docsHtml %>
61 |
62 | <% if (section.codeText.replace(/\s/gm, '') != '') { %>
63 | <%= section.codeHtml %>
64 | <% } %>
65 |
66 | <% } %>
67 |
68 |
69 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/docs/_includes/footer.html:
--------------------------------------------------------------------------------
1 |
56 |
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
Functionaljs
11 |
12 |
13 |
14 |
15 |
16 |
33 |
34 |
35 |
36 |
37 |
38 |
41 | 書籍「関数型プログラミングの基礎」(リックテレコム社)のテストコードを掲載しています。
42 |
43 |
44 |
54 |
55 |
56 | 付録
57 |
58 | 書籍で取りあげることのできなかった発展的なコードを紹介します。
59 |
60 |
65 |
66 |
67 |
68 |
69 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/test/not_in_use/chap01.spec.js:
--------------------------------------------------------------------------------
1 |
2 | it('チューリング機械で入力を複製する', (next) => {
3 | /* #@range_begin(turing_example) */
4 | var program = {
5 | 'q0': {"1": {"write": "B", "move": 1, "next": 'q1'}},
6 | 'q1': {"1": {"write": "1", "move": 1, "next": 'q1'},
7 | "0": {"write": "0", "move": 1, "next": 'q2'},
8 | "B": {"write": "0", "move": 1, "next": 'q2'}},
9 | 'q2': {"1": {"write": "1", "move": 1, "next": 'q2'},
10 | "B": {"write": "1", "move": -1, "next": 'q3'}},
11 | 'q3': {"1": {"write": "1", "move": -1, "next": 'q3'},
12 | "0": {"write": "0", "move": -1, "next": 'q3'},
13 | "B": {"write": "1", "move": 1, "next": 'q4'}},
14 | 'q4': {"1": {"write": "B", "move": 1, "next": 'q1'},
15 | "0": {"write": "0", "move": 1, "next": 'q5'}}
16 | };
17 | var tape = {
18 | '0': '1',
19 | '1':'1',
20 | '2':'1'
21 | };
22 | var result = machine(program, // プログラム
23 | tape, // テープ
24 | 'q0', // 初期状態
25 | 'q5'); // 終了状態
26 | expect(
27 | result
28 | ).to.eql(
29 | ['1','1','1','0','1','1','1' ]
30 | );
31 | /* #@range_end(turing_example) */
32 | next();
33 | });
34 |
35 | it('sumsquaresとsquareの簡約', (next) => {
36 | /* #@range_begin(sumsquares) */
37 | var sumsquares = (x,y) => {
38 | return square(x) + square(y);
39 | };
40 | var square = (x) => {
41 | return x * x;
42 | };
43 | /* #@range_end(sumsquares) */
44 | expect(
45 | sumsquares(3,4)
46 | ).to.eql(
47 | 25
48 | );
49 | next();
50 | });
51 |
52 | // #### 変数の置換ルール
53 | it('変数の置換ルール', (next) => {
54 | /* #@range_begin(variable_and_function) */
55 | var foo = () => {
56 | return 3;
57 | };
58 | expect(
59 | foo()
60 | ).to.eql(
61 | 3
62 | );
63 | /* #@range_end(variable_and_function) */
64 | /* #@range_begin(local_variable_usage_example) */
65 | var bar = () => {
66 | var foo = 3; // 変数fooに値3をバインドする
67 | return foo * 10; // 変数fooの値を10倍にする
68 | };
69 | /* #@range_end(local_variable_usage_example) */
70 | expect(
71 | bar()
72 | ).to.eql(
73 | 30
74 | );
75 | /* #@range_begin(variable_and_closure) */
76 | var bar = () => {
77 | var foo = 3;
78 | return foo * 10;
79 | };
80 | var baz = ((foo) => {
81 | return foo * 10;
82 | });
83 | /* #@range_end(variable_and_closure) */
84 | expect(
85 | bar()
86 | ).to.eql(
87 | baz(3) // 仮引数fooに値3をバインドする
88 | );
89 | next();
90 | });
91 |
92 | it('adderからsucc関数を作る', (next) => {
93 | /* #@range_begin(succ_from_adder) */
94 | var adder = (m) => {
95 | return (n) => {
96 | return m + n;
97 | };
98 | };
99 | var succ = adder(1);
100 | /* #@range_end(succ_from_adder) */
101 | expect(
102 | succ(1)
103 | ).to.eql(
104 | 2
105 | );
106 | next();
107 | });
108 |
--------------------------------------------------------------------------------
/docs/_sass/_syntax-highlighting.scss:
--------------------------------------------------------------------------------
1 | /**
2 | * Syntax highlighting styles
3 | */
4 | .highlight {
5 | background: #fff;
6 | @extend %vertical-rhythm;
7 |
8 | .c { color: #998; font-style: italic } // Comment
9 | .err { color: #a61717; background-color: #e3d2d2 } // Error
10 | .k { font-weight: bold } // Keyword
11 | .o { font-weight: bold } // Operator
12 | .cm { color: #998; font-style: italic } // Comment.Multiline
13 | .cp { color: #999; font-weight: bold } // Comment.Preproc
14 | .c1 { color: #998; font-style: italic } // Comment.Single
15 | .cs { color: #999; font-weight: bold; font-style: italic } // Comment.Special
16 | .gd { color: #000; background-color: #fdd } // Generic.Deleted
17 | .gd .x { color: #000; background-color: #faa } // Generic.Deleted.Specific
18 | .ge { font-style: italic } // Generic.Emph
19 | .gr { color: #a00 } // Generic.Error
20 | .gh { color: #999 } // Generic.Heading
21 | .gi { color: #000; background-color: #dfd } // Generic.Inserted
22 | .gi .x { color: #000; background-color: #afa } // Generic.Inserted.Specific
23 | .go { color: #888 } // Generic.Output
24 | .gp { color: #555 } // Generic.Prompt
25 | .gs { font-weight: bold } // Generic.Strong
26 | .gu { color: #aaa } // Generic.Subheading
27 | .gt { color: #a00 } // Generic.Traceback
28 | .kc { font-weight: bold } // Keyword.Constant
29 | .kd { font-weight: bold } // Keyword.Declaration
30 | .kp { font-weight: bold } // Keyword.Pseudo
31 | .kr { font-weight: bold } // Keyword.Reserved
32 | .kt { color: #458; font-weight: bold } // Keyword.Type
33 | .m { color: #099 } // Literal.Number
34 | .s { color: #d14 } // Literal.String
35 | .na { color: #008080 } // Name.Attribute
36 | .nb { color: #0086B3 } // Name.Builtin
37 | .nc { color: #458; font-weight: bold } // Name.Class
38 | .no { color: #008080 } // Name.Constant
39 | .ni { color: #800080 } // Name.Entity
40 | .ne { color: #900; font-weight: bold } // Name.Exception
41 | .nf { color: #900; font-weight: bold } // Name.Function
42 | .nn { color: #555 } // Name.Namespace
43 | .nt { color: #000080 } // Name.Tag
44 | .nv { color: #008080 } // Name.Variable
45 | .ow { font-weight: bold } // Operator.Word
46 | .w { color: #bbb } // Text.Whitespace
47 | .mf { color: #099 } // Literal.Number.Float
48 | .mh { color: #099 } // Literal.Number.Hex
49 | .mi { color: #099 } // Literal.Number.Integer
50 | .mo { color: #099 } // Literal.Number.Oct
51 | .sb { color: #d14 } // Literal.String.Backtick
52 | .sc { color: #d14 } // Literal.String.Char
53 | .sd { color: #d14 } // Literal.String.Doc
54 | .s2 { color: #d14 } // Literal.String.Double
55 | .se { color: #d14 } // Literal.String.Escape
56 | .sh { color: #d14 } // Literal.String.Heredoc
57 | .si { color: #d14 } // Literal.String.Interpol
58 | .sx { color: #d14 } // Literal.String.Other
59 | .sr { color: #009926 } // Literal.String.Regex
60 | .s1 { color: #d14 } // Literal.String.Single
61 | .ss { color: #990073 } // Literal.String.Symbol
62 | .bp { color: #999 } // Name.Builtin.Pseudo
63 | .vc { color: #008080 } // Name.Variable.Class
64 | .vg { color: #008080 } // Name.Variable.Global
65 | .vi { color: #008080 } // Name.Variable.Instance
66 | .il { color: #099 } // Literal.Number.Integer.Long
67 | }
68 |
--------------------------------------------------------------------------------
/docs/_sass/_base.scss:
--------------------------------------------------------------------------------
1 | /**
2 | * Reset some basic elements
3 | */
4 | body, h1, h2, h3, h4, h5, h6,
5 | p, blockquote, pre, hr,
6 | dl, dd, ol, ul, figure {
7 | margin: 0;
8 | padding: 0;
9 | }
10 |
11 |
12 |
13 | /**
14 | * Basic styling
15 | */
16 | body {
17 | font-family: $base-font-family;
18 | font-size: $base-font-size;
19 | line-height: $base-line-height;
20 | font-weight: 300;
21 | color: $text-color;
22 | background-color: $background-color;
23 | -webkit-text-size-adjust: 100%;
24 | }
25 |
26 |
27 |
28 | /**
29 | * Set `margin-bottom` to maintain vertical rhythm
30 | */
31 | h1, h2, h3, h4, h5, h6,
32 | p, blockquote, pre,
33 | ul, ol, dl, figure,
34 | %vertical-rhythm {
35 | margin-bottom: $spacing-unit / 2;
36 | }
37 |
38 |
39 |
40 | /**
41 | * Images
42 | */
43 | img {
44 | max-width: 100%;
45 | vertical-align: middle;
46 | }
47 |
48 |
49 |
50 | /**
51 | * Figures
52 | */
53 | figure > img {
54 | display: block;
55 | }
56 |
57 | figcaption {
58 | font-size: $small-font-size;
59 | }
60 |
61 |
62 |
63 | /**
64 | * Lists
65 | */
66 | ul, ol {
67 | margin-left: $spacing-unit;
68 | }
69 |
70 | li {
71 | > ul,
72 | > ol {
73 | margin-bottom: 0;
74 | }
75 | }
76 |
77 |
78 |
79 | /**
80 | * Headings
81 | */
82 | h1, h2, h3, h4, h5, h6 {
83 | font-weight: 300;
84 | }
85 |
86 |
87 |
88 | /**
89 | * Links
90 | */
91 | a {
92 | color: $brand-color;
93 | text-decoration: none;
94 |
95 | &:visited {
96 | color: darken($brand-color, 15%);
97 | }
98 |
99 | &:hover {
100 | color: $text-color;
101 | text-decoration: underline;
102 | }
103 | }
104 |
105 |
106 |
107 | /**
108 | * Blockquotes
109 | */
110 | blockquote {
111 | color: $grey-color;
112 | border-left: 4px solid $grey-color-light;
113 | padding-left: $spacing-unit / 2;
114 | font-size: 18px;
115 | letter-spacing: -1px;
116 | font-style: italic;
117 |
118 | > :last-child {
119 | margin-bottom: 0;
120 | }
121 | }
122 |
123 |
124 |
125 | /**
126 | * Code formatting
127 | */
128 | pre,
129 | code {
130 | font-size: 15px;
131 | border: 1px solid $grey-color-light;
132 | border-radius: 3px;
133 | background-color: #eef;
134 | }
135 |
136 | code {
137 | padding: 1px 5px;
138 | }
139 |
140 | pre {
141 | padding: 8px 12px;
142 | overflow-x: scroll;
143 |
144 | > code {
145 | border: 0;
146 | padding-right: 0;
147 | padding-left: 0;
148 | }
149 | }
150 |
151 |
152 |
153 | /**
154 | * Wrapper
155 | */
156 | .wrapper {
157 | max-width: -webkit-calc(800px - (#{$spacing-unit} * 2));
158 | max-width: calc(800px - (#{$spacing-unit} * 2));
159 | margin-right: auto;
160 | margin-left: auto;
161 | padding-right: $spacing-unit;
162 | padding-left: $spacing-unit;
163 | @extend %clearfix;
164 |
165 | @include media-query($on-laptop) {
166 | max-width: -webkit-calc(800px - (#{$spacing-unit}));
167 | max-width: calc(800px - (#{$spacing-unit}));
168 | padding-right: $spacing-unit / 2;
169 | padding-left: $spacing-unit / 2;
170 | }
171 | }
172 |
173 |
174 |
175 | /**
176 | * Clearfix
177 | */
178 | %clearfix {
179 |
180 | &:after {
181 | content: "";
182 | display: table;
183 | clear: both;
184 | }
185 | }
186 |
187 |
188 |
189 | /**
190 | * Icons
191 | */
192 | .icon {
193 |
194 | > svg {
195 | display: inline-block;
196 | width: 16px;
197 | height: 16px;
198 | vertical-align: middle;
199 |
200 | path {
201 | fill: $grey-color;
202 | }
203 | }
204 | }
205 |
--------------------------------------------------------------------------------
/docs/stylesheets/github-light.css:
--------------------------------------------------------------------------------
1 | /*
2 | The MIT License (MIT)
3 |
4 | Copyright (c) 2015 GitHub, Inc.
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy
7 | of this software and associated documentation files (the "Software"), to deal
8 | in the Software without restriction, including without limitation the rights
9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | copies of the Software, and to permit persons to whom the Software is
11 | furnished to do so, subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be included in all
14 | copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | SOFTWARE.
23 |
24 | */
25 |
26 | .pl-c /* comment */ {
27 | color: #969896;
28 | }
29 |
30 | .pl-c1 /* constant, markup.raw, meta.diff.header, meta.module-reference, meta.property-name, support, support.constant, support.variable, variable.other.constant */,
31 | .pl-s .pl-v /* string variable */ {
32 | color: #0086b3;
33 | }
34 |
35 | .pl-e /* entity */,
36 | .pl-en /* entity.name */ {
37 | color: #795da3;
38 | }
39 |
40 | .pl-s .pl-s1 /* string source */,
41 | .pl-smi /* storage.modifier.import, storage.modifier.package, storage.type.java, variable.other, variable.parameter.function */ {
42 | color: #333;
43 | }
44 |
45 | .pl-ent /* entity.name.tag */ {
46 | color: #63a35c;
47 | }
48 |
49 | .pl-k /* keyword, storage, storage.type */ {
50 | color: #a71d5d;
51 | }
52 |
53 | .pl-pds /* punctuation.definition.string, string.regexp.character-class */,
54 | .pl-s /* string */,
55 | .pl-s .pl-pse .pl-s1 /* string punctuation.section.embedded source */,
56 | .pl-sr /* string.regexp */,
57 | .pl-sr .pl-cce /* string.regexp constant.character.escape */,
58 | .pl-sr .pl-sra /* string.regexp string.regexp.arbitrary-repitition */,
59 | .pl-sr .pl-sre /* string.regexp source.ruby.embedded */ {
60 | color: #183691;
61 | }
62 |
63 | .pl-v /* variable */ {
64 | color: #ed6a43;
65 | }
66 |
67 | .pl-id /* invalid.deprecated */ {
68 | color: #b52a1d;
69 | }
70 |
71 | .pl-ii /* invalid.illegal */ {
72 | background-color: #b52a1d;
73 | color: #f8f8f8;
74 | }
75 |
76 | .pl-sr .pl-cce /* string.regexp constant.character.escape */ {
77 | color: #63a35c;
78 | font-weight: bold;
79 | }
80 |
81 | .pl-ml /* markup.list */ {
82 | color: #693a17;
83 | }
84 |
85 | .pl-mh /* markup.heading */,
86 | .pl-mh .pl-en /* markup.heading entity.name */,
87 | .pl-ms /* meta.separator */ {
88 | color: #1d3e81;
89 | font-weight: bold;
90 | }
91 |
92 | .pl-mq /* markup.quote */ {
93 | color: #008080;
94 | }
95 |
96 | .pl-mi /* markup.italic */ {
97 | color: #333;
98 | font-style: italic;
99 | }
100 |
101 | .pl-mb /* markup.bold */ {
102 | color: #333;
103 | font-weight: bold;
104 | }
105 |
106 | .pl-md /* markup.deleted, meta.diff.header.from-file */ {
107 | background-color: #ffecec;
108 | color: #bd2c00;
109 | }
110 |
111 | .pl-mi1 /* markup.inserted, meta.diff.header.to-file */ {
112 | background-color: #eaffea;
113 | color: #55a532;
114 | }
115 |
116 | .pl-mdr /* meta.diff.range */ {
117 | color: #795da3;
118 | font-weight: bold;
119 | }
120 |
121 | .pl-mo /* meta.output */ {
122 | color: #1d3e81;
123 | }
124 |
125 |
--------------------------------------------------------------------------------
/lib/dentaku.normal.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var expect = require('expect.js');
4 | var Pair = require('../lib/pair.js');
5 | var List = require('../lib/list.js');
6 | var Parser = require('../lib/parser.js');
7 | var I = require('../lib/evaluator.js').ID;
8 | var Env = require('../lib/env.js');
9 | var PP = require('../lib/pprinter.js');
10 |
11 | const Dentaku = {
12 | // expr ::= term (+ expr | e)
13 | expr: (_) => {
14 | var add = List.fromString("+");
15 | var subtract = List.fromString("-");
16 | var add_or_subtract_parser = Parser.alt(Parser.symbol(add))(Parser.symbol(subtract));
17 | return Parser.flatMap(Dentaku.term())((t) => {
18 | return Parser.alt(Parser.flatMap(add_or_subtract_parser)((operator) => {
19 | if(List.isEqual(operator,add)) {
20 | return Parser.flatMap(Dentaku.expr())((e) => {
21 | return Parser.pure(I.Exp.add(t, e));
22 | });
23 | } else if(List.isEqual(operator, subtract)) {
24 | return Parser.flatMap(Dentaku.expr())((e) => {
25 | return Parser.pure(I.Exp.subtract(t, e));
26 | });
27 | }
28 | }))(
29 | Parser.pure(t)
30 | );
31 | });
32 | },
33 | // term ::= factor (* term | e)
34 | term: (_) => {
35 | var multiply = List.fromString("*");
36 | var divide = List.fromString("/");
37 | var multiply_or_divide_parser = Parser.alt(Parser.symbol(multiply))(Parser.symbol(divide));
38 | return Parser.flatMap(Dentaku.factor())((f) => {
39 | return Parser.alt(Parser.flatMap(multiply_or_divide_parser)((operator) => {
40 | if(List.isEqual(operator,multiply)) {
41 | return Parser.flatMap(Dentaku.expr())((e) => {
42 | return Parser.pure(I.Exp.multiply(f, e));
43 | });
44 | } else if(List.isEqual(operator, divide)) {
45 | return Parser.flatMap(Dentaku.expr())((e) => {
46 | return Parser.pure(I.Exp.divide(f, e));
47 | });
48 | }
49 | }))(
50 | Parser.pure(f)
51 | );
52 | });
53 | },
54 | // factor ::= (expr) | nat
55 | factor: (_) => {
56 | var self = this;
57 | var openParen = Parser.symbol(List.fromString("("));
58 | var closeParen = Parser.symbol(List.fromString(")"));
59 | return Parser.alt(Parser.flatMap(openParen)((_) => {
60 | return Parser.flatMap(self.expr())((e) => {
61 | return Parser.flatMap(closeParen)((_) => {
62 | return Parser.pure(e);
63 | })
64 | });
65 | }))(
66 | Parser.flatMap(Parser.numeric())((numeric) => {
67 | return Parser.pure(I.Exp.num(numeric));
68 | })
69 | )
70 | },
71 | evaluate: (inputString) => {
72 | var parseResult = Parser.parse(Dentaku.expr())(List.fromString(inputString));
73 | var env = Env.empty;
74 |
75 | return parseResult.match({
76 | empty: (_) => {
77 | return "Invalid input";
78 | },
79 | cons: (head, tail) => {
80 | return head.match({
81 | cons: (ast, remainingInput) => {
82 | return remainingInput.match({
83 | empty: (_) => {
84 | return I.evaluate(ast,env);
85 | },
86 | cons: (head, tail) => {
87 | return PP.print(tail);
88 | // throw("Unused input " + PP.print(tail));
89 | }
90 | });
91 | }
92 | })
93 | }
94 | });
95 |
96 | }
97 | };
98 |
99 | module.exports = Dentaku
100 |
--------------------------------------------------------------------------------
/lib/monad_transformer.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var expect = require('expect.js');
4 | var fs = require('fs');
5 | var List = require('./list.js');
6 | var ID = require('./monad.js').ID;
7 | var Maybe = require('./monad.js').Maybe;
8 | var Either = require('./monad.js').Either;
9 |
10 | module.exports = {
11 | // ## MaybeT
12 | // Maybeモナド変換子
13 | // ~~~haskell
14 | // newtype MaybeT m a = MaybeT { runMaybeT :: m (Maybe a) }
15 | // instance Monad m => Monad (MaybeT m) where
16 | // return = MaybeT . return . Just
17 | // x >>= f = MaybeT $ do maybe_value <- runMaybeT x
18 | // case maybe_value of
19 | // Nothing -> return Nothing
20 | // Just value -> runMaybeT $ f value
21 | //
22 | // instance Monad m => Monad (MaybeT m) where
23 | // return x = MaybeT $ return (Just x)
24 | // m >>= k = MaybeT $ do a <- runMaybeT m
25 | // case a of
26 | // Nothing -> return Nothing
27 | // Just v -> runMaybeT (k v)
28 | // fail _ = MaybeT $ return Nothing
29 | //
30 | // instance MonadTrans MaybeT where
31 | // lift m = MaybeT $ m >>= (\x -> return (Just x))
32 | // ~~~
33 | MaybeT: {
34 | // run : (m) => {
35 | // var self = this;
36 | // return (a) => {
37 | // return m(Maybe.unit(a));
38 | // };
39 | // },
40 | fail : (_) => {
41 | return (Monad) => {
42 | return Monad.unit(Maybe.nothing());
43 | };
44 | },
45 | unit : (x) => {
46 | return (Monad) => {
47 | return Monad.unit(Maybe.unit(x));
48 | // return Maybe.unit(Monad.unit(x));
49 | };
50 | },
51 | // ~~~haskell
52 | // (>>=) :: MaybeT m a -> (a -> MaybeT m b) -> MaybeT m b
53 | // ~~~
54 | flatMap: (maybeT) => { // MaybeT m a
55 | var self = this;
56 | return (f) => { // f:: a -> MaybeT m b
57 | return (Monad) => {
58 | return Monad.flatMap(maybeT)((maybeInstance) => {
59 | return Maybe.match(maybeInstance,{
60 | nothing: (_) => {
61 | return self.unit(Maybe.nothing())(Monad);
62 | },
63 | just: (v) => {
64 | return f(v);
65 | // return self.unit(Maybe.unit(f(v)))(Monad);
66 | }
67 | });
68 | });
69 | };
70 | };
71 | },
72 | // instance MonadTrans MaybeT where
73 | // lift m = MaybeT $ m >>= (\x -> return (Just x))
74 | lift: (m) => {
75 | var self = this;
76 | return m.self().flatMap(m)((x) => {
77 | return self.unit(Maybe.unit(x));
78 | });
79 | }
80 | },
81 | ErrorT: {
82 | unit: (x) => {
83 | return (Monad) => {
84 | return Monad.unit(Either.unit(x));
85 | };
86 | },
87 | // ~~~haskell
88 | // (>>=) :: ErrorT m a -> (a -> ErrorT m b) -> ErrorT m b
89 | // ~~~
90 | flatMap: (errorT) => {
91 | var self = this;
92 | return (f) => { // f:: a -> MaybeT m b
93 | return (Monad) => {
94 | return Monad.flatMap(errorT)((eitherInstance) => {
95 | return Either.match(eitherInstance,{
96 | left: (l) => {
97 | return self.unit(Either.left(l))(Monad);
98 | },
99 | right: (r) => {
100 | return f(r);
101 | }
102 | });
103 | });
104 | };
105 | };
106 | },
107 | throwError: (x) => {
108 | var self = this;
109 | return self.unit(Either.left(x));
110 | },
111 | catchError: (m) => {
112 | var self = this;
113 | return (f) => {
114 | return self.flatMap(m)((eitherInstance) => {
115 | return Either.match(eitherInstance,{
116 | left: (l) => {
117 | return f(l);
118 | },
119 | right: (r) => {
120 | return self.unit(Either.right(r));
121 | }
122 | });
123 | });
124 | };
125 | }
126 | }
127 | };
128 |
--------------------------------------------------------------------------------
/Gemfile.lock:
--------------------------------------------------------------------------------
1 | GIT
2 | remote: git://github.com/flori/json.git
3 | revision: 7f4cfd853f2c919d854fb95548a19980feff17e8
4 | branch: v1.8
5 | specs:
6 | json (1.8.6)
7 |
8 | GEM
9 | remote: https://rubygems.org/
10 | specs:
11 | RedCloth (4.2.9)
12 | activesupport (5.2.0)
13 | concurrent-ruby (~> 1.0, >= 1.0.2)
14 | i18n (>= 0.7, < 2)
15 | minitest (~> 5.1)
16 | tzinfo (~> 1.1)
17 | addressable (2.3.8)
18 | blankslate (2.1.2.4)
19 | classifier-reborn (2.0.3)
20 | fast-stemmer (~> 1.0)
21 | coffee-script (2.4.1)
22 | coffee-script-source
23 | execjs
24 | coffee-script-source (1.9.1.1)
25 | colorator (0.1)
26 | concurrent-ruby (1.0.5)
27 | diff-lcs (1.2.5)
28 | ethon (0.8.0)
29 | ffi (>= 1.3.0)
30 | execjs (2.6.0)
31 | fast-stemmer (1.0.2)
32 | ffi (1.9.10)
33 | gemoji (2.1.0)
34 | github-pages (39)
35 | RedCloth (= 4.2.9)
36 | github-pages-health-check (~> 0.2)
37 | jekyll (= 2.4.0)
38 | jekyll-coffeescript (= 1.0.1)
39 | jekyll-feed (= 0.3.1)
40 | jekyll-mentions (= 0.2.1)
41 | jekyll-redirect-from (= 0.8.0)
42 | jekyll-sass-converter (= 1.3.0)
43 | jekyll-sitemap (= 0.8.1)
44 | jemoji (= 0.5.0)
45 | kramdown (= 1.5.0)
46 | liquid (= 2.6.2)
47 | maruku (= 0.7.0)
48 | mercenary (~> 0.3)
49 | pygments.rb (= 0.6.3)
50 | rdiscount (= 2.1.7)
51 | redcarpet (= 3.3.2)
52 | terminal-table (~> 1.4)
53 | github-pages-health-check (0.5.3)
54 | addressable (~> 2.3)
55 | net-dns (~> 0.8)
56 | public_suffix (~> 1.4)
57 | typhoeus (~> 0.7)
58 | html-pipeline (1.9.0)
59 | activesupport (>= 2)
60 | nokogiri (~> 1.4)
61 | i18n (1.0.1)
62 | concurrent-ruby (~> 1.0)
63 | jekyll (2.4.0)
64 | classifier-reborn (~> 2.0)
65 | colorator (~> 0.1)
66 | jekyll-coffeescript (~> 1.0)
67 | jekyll-gist (~> 1.0)
68 | jekyll-paginate (~> 1.0)
69 | jekyll-sass-converter (~> 1.0)
70 | jekyll-watch (~> 1.1)
71 | kramdown (~> 1.3)
72 | liquid (~> 2.6.1)
73 | mercenary (~> 0.3.3)
74 | pygments.rb (~> 0.6.0)
75 | redcarpet (~> 3.1)
76 | safe_yaml (~> 1.0)
77 | toml (~> 0.1.0)
78 | jekyll-coffeescript (1.0.1)
79 | coffee-script (~> 2.2)
80 | jekyll-feed (0.3.1)
81 | jekyll-gist (1.3.5)
82 | jekyll-mentions (0.2.1)
83 | html-pipeline (~> 1.9.0)
84 | jekyll (~> 2.0)
85 | jekyll-paginate (1.1.0)
86 | jekyll-redirect-from (0.8.0)
87 | jekyll (>= 2.0)
88 | jekyll-sass-converter (1.3.0)
89 | sass (~> 3.2)
90 | jekyll-sitemap (0.8.1)
91 | jekyll-watch (1.3.0)
92 | listen (~> 3.0)
93 | jemoji (0.5.0)
94 | gemoji (~> 2.0)
95 | html-pipeline (~> 1.9)
96 | jekyll (>= 2.0)
97 | kramdown (1.5.0)
98 | liquid (2.6.2)
99 | listen (3.0.3)
100 | rb-fsevent (>= 0.9.3)
101 | rb-inotify (>= 0.9)
102 | maruku (0.7.0)
103 | mercenary (0.3.5)
104 | mini_portile2 (2.3.0)
105 | minima (1.2.0)
106 | minitest (5.11.3)
107 | net-dns (0.8.0)
108 | nokogiri (1.8.2)
109 | mini_portile2 (~> 2.3.0)
110 | parslet (1.5.0)
111 | blankslate (~> 2.0)
112 | posix-spawn (0.3.11)
113 | public_suffix (1.5.1)
114 | pygments.rb (0.6.3)
115 | posix-spawn (~> 0.3.6)
116 | yajl-ruby (~> 1.3.1)
117 | rb-fsevent (0.9.6)
118 | rb-inotify (0.9.5)
119 | ffi (>= 0.5.0)
120 | rdiscount (2.1.7)
121 | redcarpet (3.3.2)
122 | rspec (3.3.0)
123 | rspec-core (~> 3.3.0)
124 | rspec-expectations (~> 3.3.0)
125 | rspec-mocks (~> 3.3.0)
126 | rspec-core (3.3.2)
127 | rspec-support (~> 3.3.0)
128 | rspec-expectations (3.3.1)
129 | diff-lcs (>= 1.2.0, < 2.0)
130 | rspec-support (~> 3.3.0)
131 | rspec-mocks (3.3.2)
132 | diff-lcs (>= 1.2.0, < 2.0)
133 | rspec-support (~> 3.3.0)
134 | rspec-support (3.3.0)
135 | safe_yaml (1.0.4)
136 | sass (3.4.19)
137 | terminal-table (1.5.2)
138 | thread_safe (0.3.6)
139 | toml (0.1.2)
140 | parslet (~> 1.5.0)
141 | typhoeus (0.8.0)
142 | ethon (>= 0.8.0)
143 | tzinfo (1.2.5)
144 | thread_safe (~> 0.1)
145 | yajl-ruby (1.3.1)
146 |
147 | PLATFORMS
148 | ruby
149 |
150 | DEPENDENCIES
151 | github-pages
152 | json!
153 | minima
154 | rspec
155 |
156 | BUNDLED WITH
157 | 1.16.2
158 |
--------------------------------------------------------------------------------
/test/evaluator.spec.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var expect = require('expect.js');
4 | var Env = require('../lib/env.js');
5 | var emptyEnv = Env.empty;
6 |
7 | // ## IDモナドによる評価器のテスト
8 | describe('IDモナドによる評価器のテスト', () => {
9 | // 恒等モナドをIとして読み込む
10 | var I = require('../lib/evaluator.js').ID;
11 |
12 | it('数値を評価する', (next) => {
13 | expect(
14 | I.evaluate(I.Exp.num(2), emptyEnv)
15 | ).to.eql(
16 | 2
17 | );
18 | next();
19 | });
20 | it('変数の評価のテスト', (next) => {
21 | /* 変数xを1に対応させた環境を作る */
22 | var newEnv = Env.extend("x", 1, emptyEnv);
23 | expect(
24 | I.evaluate(I.Exp.variable("x"), newEnv)
25 | ).to.eql(
26 | 1
27 | );
28 | expect(
29 | I.evaluate(I.Exp.variable("y"), newEnv)
30 | ).to.be(
31 | undefined
32 | );
33 | next();
34 | });
35 | describe('演算のテスト', () => {
36 | it('足し算の評価のテスト', (next) => {
37 | /* add(1,2) */
38 | var addition = I.Exp.add(I.Exp.num(1),I.Exp.num(2));
39 | expect(
40 | I.evaluate(addition, emptyEnv)
41 | ).to.eql(
42 | 3
43 | );
44 | next();
45 | });
46 | it('かけ算の評価のテスト', (next) => {
47 | /* multiply(1,2) */
48 | var multiplication = I.Exp.add(I.Exp.num(2),I.Exp.num(3));
49 | expect(
50 | I.evaluate(multiplication, emptyEnv)
51 | ).to.eql(
52 | 5
53 | );
54 | next();
55 | });
56 | });
57 | describe('関数を評価する', () => {
58 | it('identity関数を評価する', (next) => {
59 | var identity = I.Exp.app(I.Exp.lambda(I.Exp.variable("x"),
60 | I.Exp.variable("x")),
61 | I.Exp.num(2));
62 | expect(
63 | I.evaluate(identity, emptyEnv)
64 | ).to.eql(
65 | 2
66 | );
67 | next();
68 | });
69 | it('カリー化関数の評価', (next) => {
70 | // ~~~js
71 | // ((n) => {
72 | // return (m) => {
73 | // return n + m;
74 | // };
75 | // })(2)(3)
76 | // ~~~
77 | var expression = I.Exp.app(
78 | I.Exp.app(
79 | I.Exp.lambda(I.Exp.variable("n"),
80 | I.Exp.lambda(I.Exp.variable("m"),
81 | I.Exp.add(
82 | I.Exp.variable("n"),I.Exp.variable("m")))),
83 | I.Exp.num(2)),
84 | I.Exp.num(3));
85 | expect(
86 | I.evaluate(expression, emptyEnv)
87 | ).to.eql(
88 | 5
89 | );
90 | next();
91 | });
92 | });
93 | });
94 |
95 | // ## Contモナドによる継続渡し評価器のテスト
96 | describe('Contモナドによる評価器', () => {
97 | var K = require('../lib/evaluator.js').Cont;
98 | var identity = (any) => {
99 | return any;
100 | };
101 | it('数値を評価する', (next) => {
102 | expect(
103 | K.evaluate(K.Exp.num(2), emptyEnv)(identity)
104 | ).to.eql(
105 | 2
106 | );
107 | next();
108 | });
109 | it('変数を評価する', (next) => {
110 | /* 変数xを1に対応させた環境を作る */
111 | var newEnv = Env.extend("x", 1, emptyEnv);
112 | expect(
113 | K.evaluate(K.Exp.variable("x"), newEnv)(identity)
114 | ).to.eql(
115 | 1
116 | );
117 | expect(
118 | K.evaluate(K.Exp.variable("y"), newEnv)(identity)
119 | ).to.be(
120 | undefined
121 | );
122 | next();
123 | });
124 | it('足し算の評価のテスト', (next) => {
125 | /* add(1,2) */
126 | var addition = K.Exp.add(K.Exp.num(1),K.Exp.num(2));
127 | expect(
128 | K.evaluate(addition, emptyEnv)(identity)
129 | ).to.eql(
130 | 3
131 | );
132 | next();
133 | });
134 | describe('関数を評価する', () => {
135 | it('identity関数を評価する', (next) => {
136 | var id = K.Exp.lambda(K.Exp.variable("x"),
137 | K.Exp.variable("x"));
138 | expect(
139 | K.evaluate(K.Exp.app(id,K.Exp.num(1)), emptyEnv)(identity)
140 | ).to.eql(
141 | 1
142 | );
143 | next();
144 | });
145 | it('カリー化関数の評価', (next) => {
146 | var expression = K.Exp.app(
147 | K.Exp.app(
148 | K.Exp.lambda(K.Exp.variable("n"),
149 | K.Exp.lambda(K.Exp.variable("m"),
150 | K.Exp.add(
151 | K.Exp.variable("n"),K.Exp.variable("m")))),
152 | K.Exp.num(2)),
153 | K.Exp.num(3));
154 | expect(
155 | K.evaluate(expression, emptyEnv)(identity)
156 | ).to.eql(
157 | 5
158 | );
159 | next();
160 | });
161 | });
162 | });
163 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 「関数型プログラミングの基礎」サンプルコード
2 |
3 | このレポジトリは、リックテレコム社刊行の[「関数型プログラミングの基礎」](https://www.amazon.co.jp/%E9%96%A2%E6%95%B0%E5%9E%8B%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0%E3%81%AE%E5%9F%BA%E7%A4%8E-JavaScript%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%A6%E5%AD%A6%E3%81%B6-%E7%AB%8B%E5%B7%9D%E5%AF%9F%E7%90%86/dp/4865940596/ref=sr_1_1?ie=UTF8&qid=1476598423&sr=8-1&keywords=%E9%96%A2%E6%95%B0%E5%9E%8B%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0)のサンプルコードをおさめたものです。
4 |
5 | github pagesのサイトは、http://akimichi.github.io/functionaljs/ になります。
6 |
7 | ## 利用法
8 |
9 | サンプルコードを利用するには、まず最初に本レポジトリをクローンし、次にそのディレクトリに入ります。
10 |
11 | ~~~
12 | $ git clone git@github.com:akimichi/functionaljs.git
13 | $ cd functionaljs
14 | ~~~
15 |
16 | テスト環境の構築には、 1) 個別にインストールする, 2) docker を使う 、 の2つの方法があります。
17 |
18 | ### ローカル環境にテスト環境を個別にインストールする
19 |
20 | ここでは ubuntu が動作している環境にテスト環境をインストールする方法を説明します。
21 | 他のOSについては、書籍を参照ください。
22 |
23 | #### node.js のインストール
24 |
25 | node.jsのインストールは、[nvm](https://github.com/creationix/nvm)を用います。
26 |
27 | nvmをインストールするには、curlコマンドを用いて次のようにするのが簡便です。
28 |
29 | ~~~
30 | $ curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.32.0/install.sh | bash
31 | ~~~
32 |
33 | あるいはwgetコマンドを用いる場合は、次のようになります。
34 |
35 | ~~~
36 | $ wget -qO- https://raw.githubusercontent.com/creationix/nvm/v0.32.0/install.sh | bash
37 | ~~~
38 |
39 | 上記の方法はスクリプトが移動したなどの理由で失敗することがあります。
40 | もしインストールに失敗する場合は、https://github.com/creationix/nvm を参考にして最新の方法でインストールしてください。
41 |
42 | 次に、node.js v0.12.0 をインストールします。
43 |
44 | ~~~
45 | $ nvm install v0.12.0
46 | ~~~
47 |
48 | ### テストの実行
49 |
50 | ローカル環境に nvm がインストールされていることが必要です。
51 |
52 | レポジトリをクローンしたディレクトリに移動します。
53 |
54 | ~~~
55 | cd functionajs
56 | ~~~
57 |
58 | nvm use で node.jsのバージョン v0.12.0 を使うことを指定します。
59 | npm install で必要なパッケージをインストールします。
60 |
61 | ~~~
62 | $ nvm use
63 | $ npm install
64 | ~~~
65 |
66 | テストを実行するには [gulp](http://gulpjs.com/) を利用します。
67 | gulpコマンドを利用するため、以下のようにgulpをグローバルにインストールします。
68 |
69 | ~~~
70 | $ npm install -g gulp
71 | ~~~
72 |
73 | これでJavaScriptのコードをテストする準備が整いました。
74 | コードのテストは、以下のようにします。
75 |
76 | ~~~
77 | $ gulp js-test
78 | ~~~
79 |
80 | なおscalaとhaskellのコードをテストするには、以下のdockerによるインストールを参照ください。
81 |
82 | ### dockerを使う
83 |
84 | [docker](https://www.docker.com/)を使って、各種のテスト環境を構築できます。
85 | あらかじめdockerをインストールしておいてください。
86 | この方法は、dockerさえインストールしておけば確実に環境を構築できるという利点があります。
87 | ただし、dockerイメージは約4GBあるのでディスク容量の少ないマシンでの利用には注意してください。
88 |
89 | なお、dockerを使ったインストールは、リックテレコム社が推奨する方法では**ありません**。
90 | インストール時の問題等のついては、 https://github.com/akimichi/functionaljs/issues に投稿してください。
91 |
92 | #### dockerイメージを準備する
93 |
94 | dockerコンテナを動かす前に、dockerイメージを準備する必要があります。
95 | dockerイメージを取得するには、2つの方法があります。
96 | ひとつは docker hub からダウンロードする方法で、もうひとつはDockerfileから自分で作成する方法です。
97 |
98 | ##### docker hubから取得する
99 |
100 | docker hubからイメージをダウンロードするには、pullコマンドを利用します。
101 |
102 | ~~~
103 | $ docker pull emile/functionaljs:v1
104 | ~~~
105 |
106 | ##### dockerのイメージを自分で作成する
107 |
108 | イメージを準備するもう一つの方法は、Dockerfileをもとに自分でdockerイメージを作成する方法です。
109 | イメージをローカル環境に作成するには、dockerコマンドで以下のようにします。
110 |
111 | ~~~
112 | $ docker build -t="username/functionaljs:v1" .
113 | ~~~
114 |
115 | なお、上記コマンドのusernameには基本的にユーザー名がはいります。
116 |
117 | #### 単体テストを実行する
118 |
119 | dockerイメージが準備できれば、そのイメージからコンテナを起動することでテストの実行が可能です。
120 |
121 | 以下のコマンドでnode.jsのコードをテストします。
122 | もし自分のユーザー名でイメージを作成した場合は、以下のemileの箇所を、そのユーザー名に置きかえてください。
123 |
124 | ~~~
125 | $ docker run -it --rm --workdir="/workspace/nodejs" emile/functionaljs:v1 /bin/bash --login -i -c "gulp --harmony js-test"
126 | ~~~
127 |
128 | 以下のコマンドでscala のコードをテストします。
129 |
130 | ~~~
131 | $ docker run -it --rm --workdir="/workspace/scala" emile/functionaljs:v1 /bin/bash -c "sbt test"
132 | ~~~
133 |
134 | 以下のコマンドでhaskell のコードをテストします。
135 |
136 | ~~~
137 | $ docker run -it --rm --workdir="/workspace/haskell" emile/functionaljs:v1 /bin/bash -c "stack test"
138 | ~~~
139 |
140 | #### REPLを実行する
141 |
142 | dockerコンテナにログインすることで、node.jsなどの対話的環境(REPL)を実行できます。
143 |
144 | REPL を実行するには、まずコンテナにログインします。
145 |
146 | ~~~
147 | $ docker run -it --workdir="/workspace" emile/functionaljs:v1 bash --login -i
148 | ~~~
149 |
150 | これでコンテナ内の /workspace ディレクトリにログインしました。
151 |
152 | node.jsのREPLを試すには、以下のように nodejsのディレクトリに移動して nodeコマンド を呼びだします。
153 | コンソールを抜けるときは Ctrl-C を2回続けて押します。
154 |
155 | ~~~
156 | $ cd nodejs/
157 | $ node
158 | > 1 + 2
159 | 3
160 | >
161 | (^C again to quit)
162 | ~~~
163 |
164 | scala のREPLを試すには、 scalaのディレクトリに移動して、 sbt console を起動します。
165 | コンソールを抜けるには、:q を入力します。
166 |
167 | ~~~
168 | $ cd scala
169 | $ sbt console
170 |
171 | [info] Loading project definition from /workspace/scala/project
172 | [info] Set current project to Functional Book Test Project (in build file:/workspace/scala/)
173 | [info] Starting scala interpreter...
174 | [info]
175 | Welcome to Scala version 2.9.1.final (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_80).
176 | Type in expressions to have them evaluated.
177 | Type :help for more information.
178 |
179 | scala> 1 + 2
180 | res0: Int = 3
181 |
182 | scala> :q
183 | ~~~
184 |
185 | haskellのREPLを試すには、 haskell のディレクトリに移動して、 ghci を起動します。
186 | コンソールを抜けるには、:q を入力します。
187 |
188 | ~~~
189 | $ cd haskell
190 | $ ghci
191 |
192 | GHCi, version 7.10.2: http://www.haskell.org/ghc/ :? for help
193 | Prelude> 1 + 2
194 | 3
195 | Prelude> :q
196 | Leaving GHCi.
197 | ~~~
198 |
199 |
200 |
201 |
--------------------------------------------------------------------------------
/docs/_sass/_layout.scss:
--------------------------------------------------------------------------------
1 | /**
2 | * Site header
3 | */
4 | .site-header {
5 | border-top: 5px solid $grey-color-dark;
6 | border-bottom: 1px solid $grey-color-light;
7 | min-height: 56px;
8 |
9 | // Positioning context for the mobile navigation icon
10 | position: relative;
11 | }
12 |
13 | .site-title {
14 | font-size: 26px;
15 | line-height: 56px;
16 | letter-spacing: -1px;
17 | margin-bottom: 0;
18 | float: left;
19 |
20 | &,
21 | &:visited {
22 | color: $grey-color-dark;
23 | }
24 | }
25 |
26 | .site-nav {
27 | float: right;
28 | line-height: 56px;
29 |
30 | .menu-icon {
31 | display: none;
32 | }
33 |
34 | .page-link {
35 | color: $text-color;
36 | line-height: $base-line-height;
37 |
38 | // Gaps between nav items, but not on the first one
39 | &:not(:first-child) {
40 | margin-left: 20px;
41 | }
42 | }
43 |
44 | @include media-query($on-palm) {
45 | position: absolute;
46 | top: 9px;
47 | right: 30px;
48 | background-color: $background-color;
49 | border: 1px solid $grey-color-light;
50 | border-radius: 5px;
51 | text-align: right;
52 |
53 | .menu-icon {
54 | display: block;
55 | float: right;
56 | width: 36px;
57 | height: 26px;
58 | line-height: 0;
59 | padding-top: 10px;
60 | text-align: center;
61 |
62 | > svg {
63 | width: 18px;
64 | height: 15px;
65 |
66 | path {
67 | fill: $grey-color-dark;
68 | }
69 | }
70 | }
71 |
72 | .trigger {
73 | clear: both;
74 | display: none;
75 | }
76 |
77 | &:hover .trigger {
78 | display: block;
79 | padding-bottom: 5px;
80 | }
81 |
82 | .page-link {
83 | display: block;
84 | padding: 5px 10px;
85 | }
86 | }
87 | }
88 |
89 |
90 |
91 | /**
92 | * Site footer
93 | */
94 | .site-footer {
95 | border-top: 1px solid $grey-color-light;
96 | padding: $spacing-unit 0;
97 | }
98 |
99 | .footer-heading {
100 | font-size: 18px;
101 | margin-bottom: $spacing-unit / 2;
102 | }
103 |
104 | .contact-list,
105 | .social-media-list {
106 | list-style: none;
107 | margin-left: 0;
108 | }
109 |
110 | .footer-col-wrapper {
111 | font-size: 15px;
112 | color: $grey-color;
113 | margin-left: -$spacing-unit / 2;
114 | @extend %clearfix;
115 | }
116 |
117 | .footer-col {
118 | float: left;
119 | margin-bottom: $spacing-unit / 2;
120 | padding-left: $spacing-unit / 2;
121 | }
122 |
123 | .footer-col-1 {
124 | width: -webkit-calc(35% - (#{$spacing-unit} / 2));
125 | width: calc(35% - (#{$spacing-unit} / 2));
126 | }
127 |
128 | .footer-col-2 {
129 | width: -webkit-calc(20% - (#{$spacing-unit} / 2));
130 | width: calc(20% - (#{$spacing-unit} / 2));
131 | }
132 |
133 | .footer-col-3 {
134 | width: -webkit-calc(45% - (#{$spacing-unit} / 2));
135 | width: calc(45% - (#{$spacing-unit} / 2));
136 | }
137 |
138 | @include media-query($on-laptop) {
139 | .footer-col-1,
140 | .footer-col-2 {
141 | width: -webkit-calc(50% - (#{$spacing-unit} / 2));
142 | width: calc(50% - (#{$spacing-unit} / 2));
143 | }
144 |
145 | .footer-col-3 {
146 | width: -webkit-calc(100% - (#{$spacing-unit} / 2));
147 | width: calc(100% - (#{$spacing-unit} / 2));
148 | }
149 | }
150 |
151 | @include media-query($on-palm) {
152 | .footer-col {
153 | float: none;
154 | width: -webkit-calc(100% - (#{$spacing-unit} / 2));
155 | width: calc(100% - (#{$spacing-unit} / 2));
156 | }
157 | }
158 |
159 |
160 |
161 | /**
162 | * Page content
163 | */
164 | .page-content {
165 | padding: $spacing-unit 0;
166 | }
167 |
168 | .page-heading {
169 | font-size: 20px;
170 | }
171 |
172 | .post-list {
173 | margin-left: 0;
174 | list-style: none;
175 |
176 | > li {
177 | margin-bottom: $spacing-unit;
178 | }
179 | }
180 |
181 | .post-meta {
182 | font-size: $small-font-size;
183 | color: $grey-color;
184 | }
185 |
186 | .post-link {
187 | display: block;
188 | font-size: 24px;
189 | }
190 |
191 |
192 |
193 | /**
194 | * Posts
195 | */
196 | .post-header {
197 | margin-bottom: $spacing-unit;
198 | }
199 |
200 | .post-title {
201 | font-size: 42px;
202 | letter-spacing: -1px;
203 | line-height: 1;
204 |
205 | @include media-query($on-laptop) {
206 | font-size: 36px;
207 | }
208 | }
209 |
210 | .post-content {
211 | margin-bottom: $spacing-unit;
212 |
213 | h2 {
214 | font-size: 32px;
215 |
216 | @include media-query($on-laptop) {
217 | font-size: 28px;
218 | }
219 | }
220 |
221 | h3 {
222 | font-size: 26px;
223 |
224 | @include media-query($on-laptop) {
225 | font-size: 22px;
226 | }
227 | }
228 |
229 | h4 {
230 | font-size: 20px;
231 |
232 | @include media-query($on-laptop) {
233 | font-size: 18px;
234 | }
235 | }
236 | }
237 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM phusion/baseimage:0.9.11
2 | # c.f. https://github.com/phusion/baseimage-docker
3 | MAINTAINER Akimichi Tatsukawa
4 | ENV REFRESHED_AT 2016-10-24(Mon)
5 | ## Use baseimage-docker's init system.
6 | CMD ["/sbin/my_init"]
7 |
8 | RUN /etc/my_init.d/00_regen_ssh_host_keys.sh
9 |
10 | RUN sed -i~ -e 's;http://archive.ubuntu.com/ubuntu;http://ftp.jaist.ac.jp/pub/Linux/ubuntu;' /etc/apt/sources.list
11 | RUN apt-get -yqq update
12 |
13 | ## Japanese Environment
14 | RUN DEBIAN_FRONTEND=noninteractive apt-get install -y language-pack-ja
15 | ENV LANG ja_JP.UTF-8
16 | RUN update-locale LANG=ja_JP.UTF-8
17 | RUN (mv /etc/localtime /etc/localtime.org && ln -s /usr/share/zoneinfo/Asia/Tokyo /etc/localtime)
18 |
19 | ## Development Environment
20 | ENV EDITOR vim
21 | RUN update-alternatives --set editor /usr/bin/vim.basic
22 | RUN DEBIAN_FRONTEND=noninteractive apt-get install -y git wget curl unzip build-essential python-dev rake
23 |
24 | COPY .profile /root
25 | RUN mkdir -p /workspace/nodejs && \
26 | mkdir -p /workspace/scala && \
27 | mkdir -p /workspace/haskell
28 |
29 |
30 | #########################
31 | ## sbt インストール
32 | #########################
33 | ENV SCALA_VERSION 2.11.7
34 | ENV SBT_VERSION 0.13.8
35 |
36 | COPY build.sbt /workspace/scala
37 | COPY project /workspace/scala/project
38 | COPY src /workspace/scala/src
39 |
40 | # INSTALL JAVA 7 add webupd8 repository
41 | RUN \
42 | echo "===> add webupd8 repository..." && \
43 | echo "deb http://ppa.launchpad.net/webupd8team/java/ubuntu trusty main" | tee /etc/apt/sources.list.d/webupd8team-java.list && \
44 | echo "deb-src http://ppa.launchpad.net/webupd8team/java/ubuntu trusty main" | tee -a /etc/apt/sources.list.d/webupd8team-java.list && \
45 | apt-key adv --keyserver keyserver.ubuntu.com --recv-keys EEA14886 && \
46 | apt-get update && \
47 | \
48 | \
49 | echo "===> install Java" && \
50 | echo debconf shared/accepted-oracle-license-v1-1 select true | debconf-set-selections && \
51 | echo debconf shared/accepted-oracle-license-v1-1 seen true | debconf-set-selections && \
52 | DEBIAN_FRONTEND=noninteractive apt-get install -y --force-yes oracle-java7-installer oracle-java7-set-default && \
53 | \
54 | \
55 | echo "===> clean up..." && \
56 | rm -rf /var/cache/oracle-jdk7-installer && \
57 | apt-get clean && \
58 | rm -rf /var/lib/apt/lists/*
59 |
60 | # scala
61 | RUN \
62 | cd /root && \
63 | curl -o scala-$SCALA_VERSION.tgz http://downloads.typesafe.com/scala/$SCALA_VERSION/scala-$SCALA_VERSION.tgz && \
64 | tar -xf scala-$SCALA_VERSION.tgz && \
65 | rm scala-$SCALA_VERSION.tgz
66 | # sbt
67 | WORKDIR /workspace/scala
68 | RUN \
69 | curl -L -o sbt-$SBT_VERSION.deb https://dl.bintray.com/sbt/debian/sbt-$SBT_VERSION.deb && \
70 | dpkg -i sbt-$SBT_VERSION.deb && \
71 | rm sbt-$SBT_VERSION.deb && \
72 | apt-get update
73 | RUN sbt update
74 |
75 | ###############################
76 | # Install nvm with node and npm
77 | ###############################
78 | ENV NODE_VERSION 0.12.0
79 |
80 | # COPY test /workspace/nodejs/test
81 | # COPY lib /workspace/nodejs/lib
82 | # COPY .nvmrc gulpfile.js package.json /workspace/nodejs/
83 | RUN touch $HOME/.ssh/known_hosts
84 | RUN ssh-keyscan github.com >> $HOME/.ssh/known_hosts
85 | RUN git clone https://github.com/akimichi/functionaljs.git /workspace/nodejs
86 |
87 | # install node.js
88 | WORKDIR /workspace/nodejs
89 | RUN add-apt-repository ppa:chris-lea/node.js && \
90 | apt-get update -qq
91 | RUN DEBIAN_FRONTEND=noninteractive apt-get install -y nodejs
92 | # Replace shell with bash so we can source files
93 | RUN rm /bin/sh && ln -s /bin/bash /bin/sh
94 |
95 | WORKDIR /root
96 | # setup the nvm environment
97 | # Install nvm with node and npm
98 | RUN git clone https://github.com/creationix/nvm.git $HOME/.nvm
99 | RUN bash \
100 | && source $HOME/.nvm/nvm.sh \
101 | && nvm install v$NODE_VERSION \
102 | && nvm alias default v$NODE_VERSION \
103 | && nvm use default
104 |
105 | ENV NODE_PATH $NVM_DIR/v$NODE_VERSION/lib/node_modules
106 | ENV PATH $NVM_DIR/v$NODE_VERSION/bin:$PATH
107 |
108 | RUN npm install -g node-gyp && \
109 | npm install -g mocha && \
110 | npm install -g gulp && \
111 | npm install -g coffee-script
112 | WORKDIR /workspace
113 | RUN cd /workspace/nodejs && npm install
114 |
115 | #################
116 | # install haskell
117 | #################
118 | RUN export DEBIAN_FRONTEND=noninteractive && \
119 | apt-get update && \
120 | apt-get dist-upgrade -qqy && \
121 | apt-get install -qqy --no-install-recommends software-properties-common && \
122 | add-apt-repository -y ppa:hvr/ghc && \
123 | apt-get update && \
124 | apt-get install -qqy cabal-install-1.22 ghc-7.10.2 happy-1.19.5 alex-3.1.4 && \
125 | apt-get autoremove -qqy && \
126 | apt-get clean && apt-get autoclean && \
127 | rm -rf /usr/share/man/?? && rm -rf /usr/share/man/??_*
128 |
129 | ENV PATH="${HOME}/.cabal/bin:/opt/cabal/1.22/bin:/opt/ghc/7.10.2/bin:${PATH}"
130 |
131 | WORKDIR /workspace/haskell
132 | RUN wget https://www.stackage.org/lts/cabal.config
133 |
134 | # install stackage
135 | RUN apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 575159689BEFB442
136 | RUN echo 'deb http://download.fpcomplete.com/ubuntu trusty main' | tee /etc/apt/sources.list.d/fpco.list
137 | RUN apt-get update && apt-get install stack -y
138 | COPY src /workspace/haskell/src/
139 | COPY functionaljs.cabal Setup.hs LICENSE /workspace/haskell/
140 | RUN stack init
141 | RUN stack setup
142 |
143 | # cabal
144 | RUN cabal update
145 | RUN cabal install 'cabal-install >= 0.10'
146 | RUN cabal install
147 |
148 | RUN apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
149 |
150 | VOLUME /workspace
151 |
152 | CMD ["bash"]
153 |
--------------------------------------------------------------------------------
/bin/input-capitalise.js:
--------------------------------------------------------------------------------
1 | (function(exports) {
2 |
3 | "use strict";
4 |
5 | // ストリームでユーザー入力を制御する
6 |
7 | var expect = require('expect.js');
8 | // var readline = require('readline');
9 | // var rl = readline.createInterface({
10 | // input: process.stdin,
11 | // output: process.stdout
12 | // });
13 | var self = {
14 | match: (data, pattern) => {
15 | // return data(pattern);
16 | return data.call(pattern, pattern);
17 | },
18 | string: {
19 | head: (str) => {
20 | expect(str).to.a('string');
21 | return str[0];
22 | },
23 | tail: (str) => {
24 | expect(str).to.a('string');
25 | return str.substring(1);
26 | },
27 | isEmpty: (str) => {
28 | return str.length === 0;
29 | },
30 | concat: (str1, str2) => {
31 |
32 |
33 | },
34 | toArray: (str) => {
35 | expect(str).to.a('string');
36 | var glue = (item) => {
37 | return (rest) => {
38 | return [item].concat(rest);
39 | };
40 | };
41 | if(self.string.isEmpty(str)) {
42 | return [];
43 | } else {
44 | return [self.string.head(str)].concat(self.string.toArray(self.string.tail(str)));
45 | }
46 | }
47 | },
48 | stream: {
49 | empty: (_) => {
50 | return (pattern) => {
51 | expect(pattern).to.an('object');
52 | return pattern.empty();
53 | };
54 | },
55 | cons: (head,tailThunk) => {
56 | expect(tailThunk).to.a('function');
57 | return (pattern) => {
58 | expect(pattern).to.an('object');
59 | return pattern.cons(head,tailThunk);
60 | };
61 | },
62 | // head:: STREAM -> MAYBE[STREAM]
63 | head: (lazyList) => {
64 | return self.match(lazyList,{
65 | empty: (_) => {
66 | return undefined;
67 | },
68 | cons: (value, tailThunk) => {
69 | return value;
70 | }
71 | });
72 | },
73 | // tail:: STREAM -> MAYBE[STREAM]
74 | tail: (lazyList) => {
75 | return self.match(lazyList,{
76 | empty: (_) => {
77 | return undefined;
78 | },
79 | cons: (head, tailThunk) => {
80 | return tailThunk();
81 | }
82 | });
83 | },
84 | isEmpty: (lazyList) => {
85 | return self.match(lazyList,{
86 | empty: (_) => {
87 | return true;
88 | },
89 | cons: (head,tailThunk) => {
90 | return false;
91 | }
92 | });
93 | },
94 | // ## stream#map
95 | map: (lazyList) => {
96 | return (transform) => {
97 | return self.match(lazyList,{
98 | empty: (_) => {
99 | return self.stream.empty();
100 | },
101 | cons: (head,tailThunk) => {
102 | return self.stream.cons(transform(head),(_) => {
103 | return self.stream.map(tailThunk())(transform);
104 | });
105 | }
106 | });
107 | };
108 | },
109 | // ## stream#concat
110 | concat: (xs) => {
111 | return (ysThunk) => {
112 | return self.match(xs,{
113 | empty: (_) => {
114 | return ysThunk();
115 | },
116 | cons: (head,tailThunk) => {
117 | return self.stream.cons(head,(_) => {
118 | return self.stream.concat(tailThunk())(ysThunk);
119 | });
120 | }
121 | });
122 | };
123 | },
124 | // ## stream#flatten
125 | // flatten :: STREAM[STREAM[T]] => STREAM[T]
126 | flatten: (lazyList) => {
127 | return self.match(lazyList,{
128 | empty: (_) => {
129 | return self.stream.empty();
130 | },
131 | cons: (head,tailThunk) => {
132 | return self.stream.concat(head)((_) => {
133 | return self.stream.flatten(tailThunk());
134 | });
135 | }
136 | });
137 | },
138 | // ### stream#take
139 | // take:: STREAM -> NUMBER -> STREAM
140 | take: (lazyList) => {
141 | return (number) => {
142 | expect(number).to.a('number');
143 | expect(number).to.be.greaterThan(-1);
144 | return self.match(lazyList,{
145 | empty: (_) => {
146 | return self.stream.empty();
147 | },
148 | cons: (head,tailThunk) => {
149 | if(number === 0) {
150 | return self.stream.empty();
151 | } else {
152 | return self.stream.cons(head,(_) => {
153 | return self.stream.take(tailThunk())(number -1);
154 | });
155 | }
156 | }
157 | });
158 | };
159 | },
160 | /* #@range_begin(stream_filter) */
161 | filter: (lazyList) => {
162 | return (predicate) => {
163 | expect(predicate).to.a('function');
164 | return self.match(lazyList,{
165 | empty: (_) => {
166 | return self.stream.empty();
167 | },
168 | cons: (head,tailThunk) => {
169 | if(predicate(head)){
170 | return self.stream.cons(head,(_) => {
171 | return self.stream.filter(tailThunk())(predicate);
172 | });
173 | } else {
174 | return self.stream.filter(tailThunk())(predicate);
175 | }
176 | }
177 | });
178 | };
179 | },
180 | toArray: (lazyList) => {
181 | return self.match(lazyList,{
182 | empty: (_) => {
183 | return [];
184 | },
185 | cons: (head,tailThunk) => {
186 | return self.match(tailThunk(),{
187 | empty: (_) => {
188 | return [head];
189 | },
190 | cons: (head_,tailThunk_) => {
191 | return [head].concat(self.stream.toArray(tailThunk()));
192 | }
193 | });
194 | }
195 | });
196 | },
197 | // ### stream#fromList
198 | fromArray: (array) => {
199 | return array.reduce((accumulator, item) => {
200 | return self.stream.concat(accumulator)(self.stream.cons(item, (_) => {
201 | return self.stream.empty();
202 | }));
203 | });
204 | },
205 | toString: (lazyList) => {
206 | return self.match(lazyList,{
207 | empty: (_) => {
208 | return [];
209 | },
210 | cons: (head,tailThunk) => {
211 | return self.match(tailThunk(),{
212 | empty: (_) => {
213 | return "";
214 | },
215 | cons: (head_,tailThunk_) => {
216 | return head + self.stream.toString(tailThunk());
217 | }
218 | });
219 | }
220 | });
221 | },
222 | fromString: (str) => {
223 | expect(str).to.a('string');
224 | if(self.string.isEmpty(str)) {
225 | return self.stream.empty();
226 | } else {
227 | return self.stream.cons(self.string.head(str), () => {
228 | self.stream.fromString(self.string.tail(str));
229 | });
230 | }
231 | }
232 | } // stream
233 | }; // self
234 |
235 | module.exports = self;
236 |
237 | })(module.exports);
238 |
--------------------------------------------------------------------------------
/lib/evaluator.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var expect = require('expect.js');
4 | var fs = require('fs');
5 | var List = require('./list.js');
6 | var Pair = require('../lib/pair.js');
7 | var String = require('../lib/string.js');
8 | var PP = require('../lib/pprinter.js');
9 | var Env = require('../lib/env.js');
10 | var IO = require('../lib/monad.js').IO;
11 | var ID = require('../lib/monad.js').ID;
12 | var Cont = require('../lib/monad.js').Cont;
13 |
14 | const Evaluator = {
15 | ID: {
16 | Exp: {
17 | /* 式のパターンマッチ関数 */
18 | match : (data, pattern) => {
19 | return data(pattern);
20 | },
21 | /* 数値の式 */
22 | num: (value) => {
23 | return (pattern) => {
24 | return pattern.num(value);
25 | };
26 | },
27 | /* 変数の式 */
28 | variable : (name) => {
29 | return (pattern) => {
30 | return pattern.variable(name);
31 | };
32 | },
33 | /* 関数定義の式(λ式) */
34 | lambda: (variable, body) => {
35 | return (pattern) => {
36 | return pattern.lambda(variable, body);
37 | };
38 | },
39 | /* 関数適用の式 */
40 | app: (lambda, arg) => {
41 | return (pattern) => {
42 | return pattern.app(lambda, arg);
43 | };
44 | },
45 | /* 足し算の式 */
46 | add : (expL,expR) => {
47 | return (pattern) => {
48 | return pattern.add(expL, expR);
49 | };
50 | },
51 | /* 足し算の式 */
52 | subtract : (expL,expR) => {
53 | return (pattern) => {
54 | return pattern.subtract(expL, expR);
55 | };
56 | },
57 | /* 足し算の式 */
58 | divide : (expL,expR) => {
59 | return (pattern) => {
60 | return pattern.divide(expL, expR);
61 | };
62 | },
63 | /* かけ算の式 */
64 | multiply : (expL,expR) => {
65 | return (pattern) => {
66 | return pattern.multiply(expL, expR);
67 | };
68 | }
69 | },
70 | evaluate: (anExp, environment) => {
71 | return Evaluator.ID.Exp.match(anExp,{
72 | // 数値の評価
73 | num: (numericValue) => {
74 | return ID.unit(numericValue);
75 | },
76 | // 変数の評価
77 | variable: (name) => {
78 | return ID.unit(Env.lookup(name, environment));
79 | },
80 | /* 関数定義(λ式)の評価 */
81 | lambda: (variable, body) => {
82 | return Evaluator.ID.Exp.match(variable,{
83 | variable: (name) => {
84 | return ID.unit((actualArg) => {
85 | return Evaluator.ID.evaluate(body,
86 | Env.extend(name, actualArg, environment));
87 | });
88 | }
89 | });
90 | },
91 | /* 関数適用の評価 */
92 | app: (lambda, arg) => {
93 | return ID.flatMap(Evaluator.ID.evaluate(lambda, environment))((closure) => {
94 | return ID.flatMap(Evaluator.ID.evaluate(arg, environment))((actualArg) => {
95 | return closure(actualArg);
96 | });
97 | });
98 | },
99 | // 足し算の評価
100 | add: (expL, expR) => {
101 | return ID.flatMap(Evaluator.ID.evaluate(expL, environment))((valueL) => {
102 | return ID.flatMap(Evaluator.ID.evaluate(expR, environment))((valueR) => {
103 | return ID.unit(valueL + valueR);
104 | });
105 | });
106 | },
107 | // 足し算の評価
108 | subtract: (expL, expR) => {
109 | return ID.flatMap(self.evaluate(expL, environment))((valueL) => {
110 | return ID.flatMap(self.evaluate(expR, environment))((valueR) => {
111 | return ID.unit(valueL - valueR);
112 | });
113 | });
114 | },
115 | // 足し算の評価
116 | multiply: (expL, expR) => {
117 | return ID.flatMap(self.evaluate(expL, environment))((valueL) => {
118 | return ID.flatMap(self.evaluate(expR, environment))((valueR) => {
119 | return ID.unit(valueL * valueR);
120 | });
121 | });
122 | },
123 | // 足し算の評価
124 | divide: (expL, expR) => {
125 | return ID.flatMap(self.evaluate(expL, environment))((valueL) => {
126 | return ID.flatMap(self.evaluate(expR, environment))((valueR) => {
127 | return ID.unit(valueL / valueR);
128 | });
129 | });
130 | }
131 | });
132 | }
133 | },
134 | Cont: {
135 | Exp: {
136 | /* 式のパターンマッチ関数 */
137 | match : (data, pattern) => {
138 | return data(pattern);
139 | },
140 | /* 数値の式 */
141 | num: (value) => {
142 | return (pattern) => {
143 | return pattern.num(value);
144 | };
145 | },
146 | /* 変数の式 */
147 | variable : (name) => {
148 | return (pattern) => {
149 | return pattern.variable(name);
150 | };
151 | },
152 | /* 関数定義の式(λ式) */
153 | lambda: (variable, body) => {
154 | return (pattern) => {
155 | return pattern.lambda(variable, body);
156 | };
157 | },
158 | /* 関数適用の式 */
159 | app: (lambda, arg) => {
160 | return (pattern) => {
161 | return pattern.app(lambda, arg);
162 | };
163 | },
164 | /* 足し算の式 */
165 | add : (expL,expR) => {
166 | return (pattern) => {
167 | return pattern.add(expL, expR);
168 | };
169 | },
170 | /* callccの式 */
171 | callcc: (name, exp) => {
172 | return (pattern) => {
173 | return pattern.callcc(name, exp);
174 | };
175 | }
176 | },
177 | evaluate: (anExp, environment) => {
178 | return Evaluator.Cont.Exp.match(anExp,{
179 | // 数値の評価
180 | num: (numericValue) => {
181 | return Cont.unit(numericValue);
182 | },
183 | // 変数の評価
184 | variable: (name) => {
185 | return Cont.unit(Env.lookup(name, environment));
186 | },
187 | // 足し算の評価
188 | add: (expL, expR) => {
189 | return Cont.flatMap(Evaluator.Cont.evaluate(expL, environment))((valueL) => {
190 | return Cont.flatMap(Evaluator.Cont.evaluate(expR, environment))((valueR) => {
191 | return Cont.unit(valueL + valueR);
192 | });
193 | });
194 | },
195 | /* 関数定義(λ式)の評価 */
196 | lambda: (variable, body) => {
197 | return Evaluator.Cont.Exp.match(variable,{
198 | variable: (name) => {
199 | return Cont.unit((actualArg) => {
200 | return Evaluator.Cont.evaluate(body,
201 | Env.extend(name, actualArg, environment));
202 | });
203 | }
204 | });
205 | },
206 | /* 関数適用の評価 */
207 | app: (lambda, arg) => {
208 | return Cont.flatMap(Evaluator.Cont.evaluate(lambda, environment))((closure) => {
209 | return Cont.flatMap(Evaluator.Cont.evaluate(arg, environment))((actualArg) => {
210 | return closure(actualArg);
211 | });
212 | });
213 | },
214 | // callcc: (name, exp) => {
215 | // return Cont.flatMap()((_) => {
216 |
217 | // };
218 | // }
219 | });
220 | }
221 | }
222 | };
223 |
224 | module.exports = Evaluator
225 |
--------------------------------------------------------------------------------
/docs/fileio.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | fileio.js
6 |
7 |
8 |
9 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | -
28 | Jump To …
29 | +
30 |
31 |
196 |
197 |
198 |
199 |
200 |
201 | -
202 |
203 |
fileio.js
204 |
205 |
206 |
207 |
208 |
209 | -
210 |
217 |
218 |
var fs = require('fs');
219 |
220 | var read = (path) => {
221 | return fs.readFileSync(path, 'utf8');
222 | };
223 | var write = (path, content) => {
224 | return fs.writeFileSync(path, content);
225 | };
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
--------------------------------------------------------------------------------
/test/chap03.spec.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 |
4 | // 第3章 心の準備
5 | // ========
6 |
7 | // ## 小目次
8 | //
16 |
17 |
18 | var expect = require('expect.js');
19 |
20 | // ## 3.1
21 | //
22 | // > 参考資料: [DRY原則の利用: コードの重複と密結合の間](https://www.infoq.com/jp/news/2012/05/DRY-code-duplication-coupling)
23 | describe('DRY原則', () => {
24 | var add = (x, y) => {
25 | return x + y;
26 | };
27 | // **リスト3.1** 冗長なコード
28 | it('冗長なコード', (next) => {
29 | /* #@range_begin(redundant_code) */
30 | var timesForMultiply = (count, arg, memo) => {
31 | if(count > 1) {
32 | return timesForMultiply(count-1, arg, arg + memo);
33 | } else {
34 | return arg + memo;
35 | }
36 | };
37 | var multiply = (n, m) => {
38 | return timesForMultiply(n, m, 0);
39 | };
40 | var timesForExponential = (count, arg, memo) => {
41 | if(count > 1) {
42 | return timesForExponential(count-1, arg, arg * memo);
43 | } else {
44 | return arg * memo;
45 | }
46 | };
47 | var exponential = (n, m) => {
48 | return timesForExponential(m, n, 1);
49 | };
50 | /* #@range_end(redundant_code) */
51 | expect(
52 | multiply(2, 3)
53 | ).to.eql(
54 | 6
55 | );
56 | expect(
57 | exponential(2, 3)
58 | ).to.eql(
59 | 8
60 | );
61 | next();
62 | });
63 | it('DRYを適用する', (next) => {
64 | // **リスト3.3** DRYなtimes関数
65 | /* #@range_begin(dry_times) */
66 | var times = (count, arg, memo, fun) => { // 引数funを追加
67 | if(count > 1) {
68 | return times(count-1, arg, fun(arg,memo), fun);
69 | } else {
70 | return fun(arg,memo);
71 | }
72 | };
73 | /* #@range_end(dry_times) */
74 |
75 | // **リスト3.4** DRYなかけ算とべき乗
76 | /* #@range_begin(dry_functions) */
77 | var add = (n, m) => {
78 | return n + m;
79 | };
80 | /* times関数を利用してmultiply関数を定義する */
81 | var multiply = (n, m) => {
82 | return times(m, n, 0, add);
83 | };
84 | /* times関数を利用してexponential関数を定義する */
85 | var exponential = (n, m) => {
86 | return times(m, n, 1, multiply);
87 | };
88 | /* #@range_end(dry_functions) */
89 | expect(
90 | multiply(2, 3)
91 | ).to.eql(
92 | 6
93 | );
94 | expect(
95 | exponential(2, 3)
96 | ).to.eql(
97 | 8
98 | );
99 | expect(
100 | multiply(-2, 3)
101 | ).to.eql(
102 | -6
103 | );
104 | next();
105 | });
106 | });
107 |
108 | // ## 3.2
109 | describe('抽象化への指向', () => {
110 | // **リスト3.5** 関数という抽象化
111 | it('関数という抽象化', (next) => {
112 | /* #@range_begin(function_abstraction_example) */
113 | var succ = (n) => {
114 | return n + 1;
115 | };
116 | /* #@range_end(function_abstraction_example) */
117 | next();
118 | });
119 | describe('高階関数による抽象化', () => {
120 | var anArray = [2,3,5,7,11,13];
121 |
122 | // **リスト3.6** for文によるsum関数
123 | it('for文によるsum関数', (next) => {
124 | /* #@range_begin(sum_for) */
125 | var anArray = [2,3,5,7];
126 | var sum = (array) => {
127 | var result = 0;
128 | for(var index = 0; index < array.length; index++){
129 | result = result + array[index];
130 | }
131 | return result;
132 | };
133 | sum(anArray);
134 | /* #@range_end(sum_for) */
135 | expect(
136 | sum(anArray)
137 | ).to.eql(
138 | 17
139 | );
140 | next();
141 | });
142 | // **リスト3.7** forEachによるsum関数
143 | it('forEachによるsum関数', (next) => {
144 | /* #@range_begin(sum_forEach) */
145 | var sum = (array) => {
146 | /* 結果を格納する変数result */
147 | var result = 0;
148 | array.forEach((item) => {
149 | result = result + item;
150 | });
151 | return result;
152 | };
153 | /* #@range_end(sum_forEach) */
154 | expect(
155 | sum(anArray)
156 | ).to.eql(
157 | 41
158 | );
159 | next();
160 | });
161 | // **リスト3.8** reduceによるsum関数
162 | it('reduceによるsum関数', (next) => {
163 | /* #@range_begin(sum_reduce) */
164 | var sum = (array) => {
165 | return array.reduce((x, y) => {
166 | return x + y;
167 | });
168 | };
169 | /* #@range_end(sum_reduce) */
170 | expect(
171 | sum(anArray)
172 | ).to.eql(
173 | 41
174 | );
175 | next();
176 | });
177 | });
178 | });
179 |
180 | // ## 3.3
181 | describe('セマンティクスを意識する', () => {
182 | // **リスト3.9** 環境という仕組み
183 | it('環境という仕組み', (next) => {
184 | /* merge関数は、引数にわたされた2つのオブジェクトを併合する */
185 | var merge = (obj1, obj2) => {
186 | var mergedObject = {};
187 | for (var attrname in obj1) { mergedObject[attrname] = obj1[attrname]; }
188 | for (var attrname in obj2) { mergedObject[attrname] = obj2[attrname]; }
189 | return mergedObject;
190 | };
191 | //
192 | // - empty
193 | // - 空の環境
194 | // - extendEnv
195 | // - 環境に変数と値の対応を与えて、辞書を拡張する
196 | // - lookupEnv
197 | // - 変数を指定して、環境に記憶されている値を取り出す
198 | //
199 | /* #@range_begin(environment_example) */
200 | /* 空の環境 */
201 | var emptyEnv = {};
202 | /* 環境を拡張する */
203 | var extendEnv = (binding, oldEnv) => {
204 | /* merge(obj1, obj2) は
205 | obj1とobj2のオブジェクトをマージする関数のこと */
206 | return merge(binding, oldEnv);
207 | };
208 | /* 変数名に対応する値を環境から取り出す */
209 | var lookupEnv = (name, env) => {
210 | return env[name];
211 | };
212 | /* #@range_end(environment_example) */
213 | // ~~~
214 | // var a = 1;
215 | // var b = 3;
216 | // b
217 | // ~~~
218 | expect(((_) => {
219 | // **リスト3.11** リスト 3.10のセマンティクス
220 | /* #@range_begin(environment_example_usage) */
221 | /* 空の辞書を作成する */
222 | var initEnv = emptyEnv;
223 | /* var a = 1 を実行して、辞書を拡張する */
224 | var firstEnv = extendEnv({"a": 1}, initEnv);
225 | /* var b = 3 を実行して、辞書を拡張する */
226 | var secondEnv = extendEnv({"b": 3}, firstEnv);
227 | /* 辞書から b の値を参照する */
228 | lookupEnv("b", secondEnv);
229 | /* #@range_end(environment_example_usage) */
230 | return lookupEnv("b", secondEnv);
231 | })()).to.eql(
232 | 3
233 | );
234 | next();
235 | });
236 | });
237 |
238 | // ## 3.4
239 | describe('テストに親しむ', () => {
240 | // ### 単体テストの仕組み
241 | // > 参考資料: [単体テスト](https://ja.wikipedia.org/wiki/%E5%8D%98%E4%BD%93%E3%83%86%E3%82%B9%E3%83%88)
242 | describe('単体テストの仕組み', () => {
243 | // **リスト3.12** アサート関数の例
244 | //
245 | // assertライブラリを使う場合
246 | it('assertによる表明', (next) => {
247 | /* #@range_begin(assert_assertion) */
248 | var assert = require("assert");
249 | assert.equal(1 + 2, 3);
250 | /* #@range_end(assert_assertion) */
251 | next();
252 | });
253 | // expectライブラリを使う場合
254 | // > 参考資料: https://github.com/Automattic/expect.js
255 | it('expectによる表明', (next) => {
256 | /* #@range_begin(expect_assertion) */
257 | var expect = require('expect.js');
258 | expect(
259 | 1 + 2
260 | ).to.eql(
261 | 3
262 | );
263 | /* #@range_end(expect_assertion) */
264 | next();
265 | });
266 | });
267 | });
268 |
269 | // [目次に戻る](index.html) [次章に移る](chap04.spec.html)
270 |
--------------------------------------------------------------------------------
/bin/list.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var expect = require('expect.js');
4 | var string = require('./string');
5 |
6 | module.exports = {
7 | match : (data, pattern) => {
8 | var self = this;
9 | return data.call(self, pattern);
10 | },
11 | empty: (_) => {
12 | return (pattern) => {
13 | return pattern.empty();
14 | };
15 | },
16 | cons: (value, alist) => {
17 | return (pattern) => {
18 | return pattern.cons(value, alist);
19 | };
20 | },
21 | head: (alist) => {
22 | var self = this;
23 | return this.match(alist, {
24 | empty: (_) => {
25 | return null;
26 | },
27 | cons: (head, tail) => {
28 | return head;
29 | }
30 | });
31 | },
32 | tail: (alist) => {
33 | var self = this;
34 | return this.match(alist, {
35 | empty: (_) => {
36 | return null;
37 | },
38 | cons: (head, tail) => {
39 | return tail;
40 | }
41 | });
42 | },
43 | isEmpty: (alist) => {
44 | var self = this;
45 | return self.match(alist, {
46 | empty: (_) => {
47 | return true;
48 | },
49 | cons: (head, tail) => {
50 | return false;
51 | }
52 | });
53 | },
54 | // append:: LIST[T] -> LIST[T] -> LIST[T]
55 | // append [] ys = ys
56 | // append (x:xs) ys = x : (xs ++ ys)
57 | append: (xs) => {
58 | return (ys) => {
59 | return this.match(xs, {
60 | empty: (_) => {
61 | return ys;
62 | },
63 | cons: (head, tail) => {
64 | return this.cons(head, this.append(tail)(ys));
65 | }
66 | });
67 | };
68 | },
69 | // list#concat
70 | // concat:: LIST[LIST[T]] -> LIST[T]
71 | // concat [] = []
72 | // concat (xs:xss) = append(xs, xss)
73 | // or,
74 | // concat xss = foldr xss [] append
75 | concat: (xss) => {
76 | return this.match(xss,{
77 | empty: (_) => {
78 | return this.empty();
79 | },
80 | cons: (xs,xss) => {
81 | return this.append(xs,xss);
82 | }
83 | });
84 | },
85 | last: (alist) => {
86 | var self = this;
87 | return this.match(alist, {
88 | empty: (_) => {
89 | return null;
90 | },
91 | cons: (head, tail) => {
92 | return this.match(tail, {
93 | empty: (_) => {
94 | return head;
95 | },
96 | cons: (head, _) => {
97 | return this.last(tail);
98 | }
99 | });
100 | }
101 | });
102 | },
103 | // join:: LIST[LIST[T]] -> LIST[T]
104 | join: (list_of_list) => {
105 | return this.concat(list_of_list);
106 | },
107 | // foldr:: LIST[T] -> T -> FUNC[T -> LIST] -> T
108 | foldr: (alist) => {
109 | var self = this;
110 | return (accumulator) => {
111 | return (glue) => {
112 | expect(glue).to.a('function');
113 | return self.match(alist,{
114 | empty: (_) => {
115 | return accumulator;
116 | },
117 | cons: (head, tail) => {
118 | return glue(head)(self.foldr(tail)(accumulator)(glue));
119 | }
120 | });
121 | };
122 | };
123 | },
124 | // map:: LIST[T] -> FUNC[T -> T] -> LIST[T]
125 | map: (alist) => {
126 | var self = this;
127 | return (transform) => {
128 | return this.match(alist,{
129 | empty: (_) => {
130 | return this.empty();
131 | },
132 | cons: (head,tail) => {
133 | return this.cons(transform(head),this.map(tail)(transform));
134 | }
135 | });
136 | };
137 | },
138 | /* #@range_begin(list_reverse) */
139 | reverse: (alist) => {
140 | var self = this;
141 | var reverseAux = (alist, accumulator) => {
142 | return this.match(alist, {
143 | empty: (_) => {
144 | return accumulator; // 空のリストの場合は終了
145 | },
146 | cons: (head, tail) => {
147 | return reverseAux(tail, this.cons(head, accumulator));
148 | }
149 | });
150 | };
151 | return reverseAux(alist, this.empty());
152 | },
153 | /* #@range_end(list_reverse) */
154 | // ## list.filter
155 | /* #@range_begin(list_filter) */
156 | filter: (alist) => {
157 | return (predicate) => {
158 | return this.match(alist,{
159 | empty: (_) => {
160 | return this.empty();
161 | },
162 | cons: (head,tail) => {
163 | if(predicate(head)){
164 | return this.cons(head,(_) => {
165 | return this.filter(tail)(predicate);
166 | });
167 | } else {
168 | return this.filter(tail)(predicate);
169 | }
170 | }
171 | });
172 | };
173 | },
174 | // list#length
175 | length: (alist) => {
176 | return this.match(alist,{
177 | empty: (_) => {
178 | return 0;
179 | },
180 | cons: (head,tail) => {
181 | return this.foldr(alist)(0)((item) => {
182 | return (accumulator) => {
183 | return 1 + accumulator;
184 | };
185 | });
186 | }
187 | });
188 | },
189 | any: (alist) => {
190 | return (predicate) => {
191 | expect(predicate).to.a('function');
192 | return this.match(alist,{
193 | empty: (_) => {
194 | return false;
195 | },
196 | cons: (head,tail) => {
197 | if(predicate(head)) {
198 | return true;
199 | } else {
200 | return this.any(tail)(predicate);
201 | }
202 | }
203 | });
204 | // return compose(self.list.or.bind(self))(self.flip.bind(self)(self.list.map.bind(self))(predicate))(list);
205 | };
206 | },
207 | /* #@range_end(list_filter) */
208 | toArray: (alist) => {
209 | var toArrayAux = (alist,accumulator) => {
210 | return this.match(alist, {
211 | empty: (_) => {
212 | return accumulator; // 空のリストの場合は終了
213 | },
214 | cons: (head, tail) => {
215 | return toArrayAux(tail, accumulator.concat(head));
216 | }
217 | });
218 | };
219 | return toArrayAux(alist, []);
220 | },
221 | fromArray: (array) => {
222 | expect(array).to.an('array');
223 | return array.reduce((accumulator, item) => {
224 | return this.append(accumulator)(this.cons(item, this.empty()));
225 | }, this.empty());
226 | },
227 | /* #@range_begin(list_fromString) */
228 | fromString: (str) => {
229 | var self = this;
230 | expect(str).to.a('string');
231 | if(this.isEmpty(str)) {
232 | return this.empty();
233 | } else {
234 | return this.cons(string.head(str), this.fromString(this.tail(str)));
235 | }
236 | },
237 | /* #@range_end(list_fromString) */
238 | at: (alist) => {
239 | return (index) => {
240 | expect(index).to.a('number');
241 | expect(index).to.be.greaterThan(-1);
242 | if (index === 0) {
243 | return this.head(alist);
244 | } else {
245 | return this.at(this.tail(alist))(index - 1);
246 | }
247 | };
248 | },
249 | take: (alist) => {
250 | return (n) => {
251 | expect(n).to.a('number');
252 | expect(n).to.be.greaterThan(-1);
253 | if (n === 0) {
254 | return this.empty();
255 | } else {
256 | return this.cons(this.head)(this.take(this.tail)(n-1));
257 | }
258 | };
259 | },
260 | // ## list#drop
261 | // drop :: List => List
262 | drop: function(list){
263 | var self = this;
264 | return (n) => {
265 | expect(n).to.be.a('number');
266 | expect(n).to.be.greaterThan(-1);
267 | if (n === 0)
268 | return list;
269 | else {
270 | if(self.this.isEmpty.bind(self)(list))
271 | return self.this.empty;
272 | else {
273 | var tail = this.tail;
274 | return self.this.drop.bind(self)(tail)(n-1);
275 | }
276 | }
277 | };
278 | },
279 | /* #@range_begin(list_generate) */
280 | generate: (alist) => {
281 | var theList = alist;
282 | return (_) => {
283 | return this.match(theList,{
284 | empty: (_) => {
285 | return null;
286 | },
287 | cons: (head,tail) => {
288 | theList = tail;
289 | return head;
290 | }
291 | });
292 | };
293 | }
294 | /* #@range_end(list_generate) */
295 | };
296 |
--------------------------------------------------------------------------------
/docs/stylesheets/normalize.css:
--------------------------------------------------------------------------------
1 | /*! normalize.css v2.0.1 | MIT License | git.io/normalize */
2 |
3 | /* ==========================================================================
4 | HTML5 display definitions
5 | ========================================================================== */
6 |
7 | /*
8 | * Corrects `block` display not defined in IE 8/9.
9 | */
10 |
11 | article,
12 | aside,
13 | details,
14 | figcaption,
15 | figure,
16 | footer,
17 | header,
18 | hgroup,
19 | nav,
20 | section,
21 | summary {
22 | display: block;
23 | }
24 |
25 | /*
26 | * Corrects `inline-block` display not defined in IE 8/9.
27 | */
28 |
29 | audio,
30 | canvas,
31 | video {
32 | display: inline-block;
33 | }
34 |
35 | /*
36 | * Prevents modern browsers from displaying `audio` without controls.
37 | * Remove excess height in iOS 5 devices.
38 | */
39 |
40 | audio:not([controls]) {
41 | display: none;
42 | height: 0;
43 | }
44 |
45 | /*
46 | * Addresses styling for `hidden` attribute not present in IE 8/9.
47 | */
48 |
49 | [hidden] {
50 | display: none;
51 | }
52 |
53 | /* ==========================================================================
54 | Base
55 | ========================================================================== */
56 |
57 | /*
58 | * 1. Sets default font family to sans-serif.
59 | * 2. Prevents iOS text size adjust after orientation change, without disabling
60 | * user zoom.
61 | */
62 |
63 | html {
64 | font-family: sans-serif; /* 1 */
65 | -webkit-text-size-adjust: 100%; /* 2 */
66 | -ms-text-size-adjust: 100%; /* 2 */
67 | }
68 |
69 | /*
70 | * Removes default margin.
71 | */
72 |
73 | body {
74 | margin: 0;
75 | }
76 |
77 | /* ==========================================================================
78 | Links
79 | ========================================================================== */
80 |
81 | /*
82 | * Addresses `outline` inconsistency between Chrome and other browsers.
83 | */
84 |
85 | a:focus {
86 | outline: thin dotted;
87 | }
88 |
89 | /*
90 | * Improves readability when focused and also mouse hovered in all browsers.
91 | */
92 |
93 | a:active,
94 | a:hover {
95 | outline: 0;
96 | }
97 |
98 | /* ==========================================================================
99 | Typography
100 | ========================================================================== */
101 |
102 | /*
103 | * Addresses `h1` font sizes within `section` and `article` in Firefox 4+,
104 | * Safari 5, and Chrome.
105 | */
106 |
107 | h1 {
108 | font-size: 2em;
109 | }
110 |
111 | /*
112 | * Addresses styling not present in IE 8/9, Safari 5, and Chrome.
113 | */
114 |
115 | abbr[title] {
116 | border-bottom: 1px dotted;
117 | }
118 |
119 | /*
120 | * Addresses style set to `bolder` in Firefox 4+, Safari 5, and Chrome.
121 | */
122 |
123 | b,
124 | strong {
125 | font-weight: bold;
126 | }
127 |
128 | /*
129 | * Addresses styling not present in Safari 5 and Chrome.
130 | */
131 |
132 | dfn {
133 | font-style: italic;
134 | }
135 |
136 | /*
137 | * Addresses styling not present in IE 8/9.
138 | */
139 |
140 | mark {
141 | background: #ff0;
142 | color: #000;
143 | }
144 |
145 |
146 | /*
147 | * Corrects font family set oddly in Safari 5 and Chrome.
148 | */
149 |
150 | code,
151 | kbd,
152 | pre,
153 | samp {
154 | font-family: monospace, serif;
155 | font-size: 1em;
156 | }
157 |
158 | /*
159 | * Improves readability of pre-formatted text in all browsers.
160 | */
161 |
162 | pre {
163 | white-space: pre;
164 | white-space: pre-wrap;
165 | word-wrap: break-word;
166 | }
167 |
168 | /*
169 | * Sets consistent quote types.
170 | */
171 |
172 | q {
173 | quotes: "\201C" "\201D" "\2018" "\2019";
174 | }
175 |
176 | /*
177 | * Addresses inconsistent and variable font size in all browsers.
178 | */
179 |
180 | small {
181 | font-size: 80%;
182 | }
183 |
184 | /*
185 | * Prevents `sub` and `sup` affecting `line-height` in all browsers.
186 | */
187 |
188 | sub,
189 | sup {
190 | font-size: 75%;
191 | line-height: 0;
192 | position: relative;
193 | vertical-align: baseline;
194 | }
195 |
196 | sup {
197 | top: -0.5em;
198 | }
199 |
200 | sub {
201 | bottom: -0.25em;
202 | }
203 |
204 | /* ==========================================================================
205 | Embedded content
206 | ========================================================================== */
207 |
208 | /*
209 | * Removes border when inside `a` element in IE 8/9.
210 | */
211 |
212 | img {
213 | border: 0;
214 | }
215 |
216 | /*
217 | * Corrects overflow displayed oddly in IE 9.
218 | */
219 |
220 | svg:not(:root) {
221 | overflow: hidden;
222 | }
223 |
224 | /* ==========================================================================
225 | Figures
226 | ========================================================================== */
227 |
228 | /*
229 | * Addresses margin not present in IE 8/9 and Safari 5.
230 | */
231 |
232 | figure {
233 | margin: 0;
234 | }
235 |
236 | /* ==========================================================================
237 | Forms
238 | ========================================================================== */
239 |
240 | /*
241 | * Define consistent border, margin, and padding.
242 | */
243 |
244 | fieldset {
245 | border: 1px solid #c0c0c0;
246 | margin: 0 2px;
247 | padding: 0.35em 0.625em 0.75em;
248 | }
249 |
250 | /*
251 | * 1. Corrects color not being inherited in IE 8/9.
252 | * 2. Remove padding so people aren't caught out if they zero out fieldsets.
253 | */
254 |
255 | legend {
256 | border: 0; /* 1 */
257 | padding: 0; /* 2 */
258 | }
259 |
260 | /*
261 | * 1. Corrects font family not being inherited in all browsers.
262 | * 2. Corrects font size not being inherited in all browsers.
263 | * 3. Addresses margins set differently in Firefox 4+, Safari 5, and Chrome
264 | */
265 |
266 | button,
267 | input,
268 | select,
269 | textarea {
270 | font-family: inherit; /* 1 */
271 | font-size: 100%; /* 2 */
272 | margin: 0; /* 3 */
273 | }
274 |
275 | /*
276 | * Addresses Firefox 4+ setting `line-height` on `input` using `!important` in
277 | * the UA stylesheet.
278 | */
279 |
280 | button,
281 | input {
282 | line-height: normal;
283 | }
284 |
285 | /*
286 | * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
287 | * and `video` controls.
288 | * 2. Corrects inability to style clickable `input` types in iOS.
289 | * 3. Improves usability and consistency of cursor style between image-type
290 | * `input` and others.
291 | */
292 |
293 | button,
294 | html input[type="button"], /* 1 */
295 | input[type="reset"],
296 | input[type="submit"] {
297 | -webkit-appearance: button; /* 2 */
298 | cursor: pointer; /* 3 */
299 | }
300 |
301 | /*
302 | * Re-set default cursor for disabled elements.
303 | */
304 |
305 | button[disabled],
306 | input[disabled] {
307 | cursor: default;
308 | }
309 |
310 | /*
311 | * 1. Addresses box sizing set to `content-box` in IE 8/9.
312 | * 2. Removes excess padding in IE 8/9.
313 | */
314 |
315 | input[type="checkbox"],
316 | input[type="radio"] {
317 | box-sizing: border-box; /* 1 */
318 | padding: 0; /* 2 */
319 | }
320 |
321 | /*
322 | * 1. Addresses `appearance` set to `searchfield` in Safari 5 and Chrome.
323 | * 2. Addresses `box-sizing` set to `border-box` in Safari 5 and Chrome
324 | * (include `-moz` to future-proof).
325 | */
326 |
327 | input[type="search"] {
328 | -webkit-appearance: textfield; /* 1 */
329 | -moz-box-sizing: content-box;
330 | -webkit-box-sizing: content-box; /* 2 */
331 | box-sizing: content-box;
332 | }
333 |
334 | /*
335 | * Removes inner padding and search cancel button in Safari 5 and Chrome
336 | * on OS X.
337 | */
338 |
339 | input[type="search"]::-webkit-search-cancel-button,
340 | input[type="search"]::-webkit-search-decoration {
341 | -webkit-appearance: none;
342 | }
343 |
344 | /*
345 | * Removes inner padding and border in Firefox 4+.
346 | */
347 |
348 | button::-moz-focus-inner,
349 | input::-moz-focus-inner {
350 | border: 0;
351 | padding: 0;
352 | }
353 |
354 | /*
355 | * 1. Removes default vertical scrollbar in IE 8/9.
356 | * 2. Improves readability and alignment in all browsers.
357 | */
358 |
359 | textarea {
360 | overflow: auto; /* 1 */
361 | vertical-align: top; /* 2 */
362 | }
363 |
364 | /* ==========================================================================
365 | Tables
366 | ========================================================================== */
367 |
368 | /*
369 | * Remove most spacing between table cells.
370 | */
371 |
372 | table {
373 | border-collapse: collapse;
374 | border-spacing: 0;
375 | }
--------------------------------------------------------------------------------