trackler 2.2.1.15 → 2.2.1.16
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/trackler/version.rb +1 -1
- data/problem-specifications/TOPICS.txt +8 -1
- data/tracks/c/docs/SNIPPET.txt +11 -0
- data/tracks/ceylon/docs/SNIPPET.txt +3 -0
- data/tracks/clojure/docs/SNIPPET.txt +5 -0
- data/tracks/csharp/build.cake +23 -16
- data/tracks/csharp/build.ps1 +1 -1
- data/tracks/csharp/build.sh +1 -1
- data/tracks/csharp/exercises/pangram/PangramTest.cs +9 -18
- data/tracks/csharp/generators/.gitignore +2 -1
- data/tracks/csharp/generators/Exercise.cs +11 -12
- data/tracks/csharp/generators/Exercises/AllYourBase.cs +2 -2
- data/tracks/csharp/generators/Exercises/Allergies.cs +1 -1
- data/tracks/csharp/generators/Exercises/Alphametics.cs +1 -1
- data/tracks/csharp/generators/Exercises/Anagram.cs +1 -1
- data/tracks/csharp/generators/Exercises/BeerSong.cs +1 -1
- data/tracks/csharp/generators/Exercises/BinarySearch.cs +1 -1
- data/tracks/csharp/generators/Exercises/BookStore.cs +1 -1
- data/tracks/csharp/generators/Exercises/BracketPush.cs +1 -1
- data/tracks/csharp/generators/Exercises/CollatzConjecture.cs +2 -2
- data/tracks/csharp/generators/Exercises/CryptoSquare.cs +1 -1
- data/tracks/csharp/generators/Exercises/FoodChain.cs +1 -1
- data/tracks/csharp/generators/Exercises/Gigasecond.cs +1 -1
- data/tracks/csharp/generators/Exercises/Hamming.cs +1 -1
- data/tracks/csharp/generators/Exercises/House.cs +1 -1
- data/tracks/csharp/generators/Exercises/Leap.cs +1 -1
- data/tracks/csharp/generators/Exercises/Luhn.cs +1 -1
- data/tracks/csharp/generators/Exercises/NthPrime.cs +1 -1
- data/tracks/csharp/generators/Exercises/Pangram.cs +1 -11
- data/tracks/csharp/generators/Exercises/PerfectNumbers.cs +1 -1
- data/tracks/csharp/generators/Exercises/PhoneNumber.cs +1 -1
- data/tracks/csharp/generators/Exercises/RailFenceCipher.cs +1 -1
- data/tracks/csharp/generators/Exercises/RnaTranscription.cs +1 -1
- data/tracks/csharp/generators/Exercises/RomanNumerals.cs +1 -1
- data/tracks/csharp/generators/Exercises/RunLengthEncoding.cs +1 -1
- data/tracks/csharp/generators/Exercises/Say.cs +2 -7
- data/tracks/csharp/generators/Exercises/SecretHandshake.cs +1 -1
- data/tracks/csharp/generators/Exercises/Sieve.cs +1 -1
- data/tracks/csharp/generators/Exercises/SpaceAge.cs +1 -1
- data/tracks/csharp/generators/Exercises/SumOfMultiples.cs +1 -1
- data/tracks/csharp/generators/Exercises/Transpose.cs +1 -1
- data/tracks/csharp/generators/Exercises/WordCount.cs +1 -1
- data/tracks/csharp/generators/Exercises/Wordy.cs +1 -1
- data/tracks/csharp/generators/Output/FormattingExtensions.cs +4 -2
- data/tracks/csharp/generators/Output/ValueFormatter.cs +1 -1
- data/tracks/delphi/docs/SNIPPET.txt +6 -13
- data/tracks/ecmascript/docs/SNIPPET.txt +7 -0
- data/tracks/elisp/docs/SNIPPET.txt +13 -0
- data/tracks/elixir/config.json +2 -2
- data/tracks/elixir/docs/SNIPPET.txt +10 -0
- data/tracks/erlang/docs/SNIPPET.txt +8 -0
- data/tracks/fsharp/docs/SNIPPET.txt +3 -0
- data/tracks/go/config.json +174 -174
- data/tracks/go/config/maintainers.json +6 -9
- data/tracks/go/docs/SNIPPET.txt +6 -0
- data/tracks/groovy/config.json +4 -4
- data/tracks/groovy/docs/SNIPPET.txt +12 -0
- data/tracks/haskell/config.json +2 -0
- data/tracks/haskell/docs/SNIPPET.txt +4 -0
- data/tracks/java/docs/SNIPPET.txt +7 -0
- data/tracks/java/exercises/robot-simulator/src/example/java/GridPosition.java +21 -16
- data/tracks/java/exercises/robot-simulator/src/test/java/RobotTest.java +24 -24
- data/tracks/javascript/docs/SNIPPET.txt +10 -0
- data/tracks/kotlin/docs/SNIPPET.txt +3 -0
- data/tracks/kotlin/exercises/change/src/example/kotlin/ChangeCalculator.kt +35 -0
- data/tracks/kotlin/exercises/change/src/test/kotlin/ChangeCalculatorTest.kt +105 -0
- data/tracks/lfe/docs/SNIPPET.txt +12 -0
- data/tracks/lua/docs/SNIPPET.txt +7 -0
- data/tracks/mips/docs/SNIPPET.txt +23 -0
- data/tracks/ocaml/docs/SNIPPET.txt +1 -0
- data/tracks/purescript/config.json +1 -1
- data/tracks/purescript/docs/SNIPPET.txt +8 -0
- data/tracks/python/exercises/saddle-points/README.md +1 -1
- data/tracks/racket/docs/SNIPPET.txt +6 -0
- data/tracks/ruby/docs/SNIPPET.txt +5 -0
- data/tracks/rust/docs/SNIPPET.txt +3 -0
- data/tracks/scala/docs/SNIPPET.txt +4 -0
- data/tracks/scala/exercises/ocr-numbers/example.scala +17 -11
- data/tracks/scala/exercises/ocr-numbers/src/test/scala/OcrNumbersTest.scala +153 -0
- data/tracks/scala/exercises/pig-latin/example.scala +11 -6
- data/tracks/scala/exercises/pig-latin/src/test/scala/PigLatinTest.scala +80 -10
- data/tracks/scala/testgen/src/main/scala/OcrNumbersTestGenerator.scala +34 -0
- data/tracks/scala/testgen/src/main/scala/PigLatinTestGenerator.scala +16 -0
- data/tracks/scheme/docs/SNIPPET.txt +7 -0
- data/tracks/sml/bin/generate +277 -0
- data/tracks/sml/config.json +30 -0
- data/tracks/sml/docs/ABOUT.md +32 -0
- data/tracks/sml/docs/LEARNING.md +37 -0
- data/tracks/sml/docs/SNIPPET.txt +3 -0
- data/tracks/sml/exercises/bob/README.md +48 -0
- data/tracks/sml/exercises/bob/bob.sml +2 -0
- data/tracks/sml/exercises/bob/example.sml +22 -0
- data/tracks/sml/exercises/bob/test.sml +87 -0
- data/tracks/sml/exercises/bob/testlib.sml +159 -0
- data/tracks/sml/exercises/difference-of-squares/README.md +49 -0
- data/tracks/sml/exercises/difference-of-squares/difference-of-squares.sml +8 -0
- data/tracks/sml/exercises/difference-of-squares/example.sml +11 -0
- data/tracks/sml/exercises/difference-of-squares/test.sml +45 -0
- data/tracks/sml/exercises/difference-of-squares/testlib.sml +159 -0
- data/tracks/sml/exercises/hello-world/README.md +51 -0
- data/tracks/sml/exercises/hello-world/example.sml +1 -0
- data/tracks/sml/exercises/hello-world/hello-world.sml +2 -0
- data/tracks/sml/exercises/hello-world/test.sml +15 -0
- data/tracks/sml/exercises/hello-world/testlib.sml +159 -0
- data/tracks/sml/lib/testlib.sml +159 -0
- data/tracks/swift/docs/SNIPPET.txt +9 -0
- data/tracks/typescript/docs/SNIPPET.txt +8 -0
- metadata +53 -6
- data/tracks/csharp/generators/Generators.csproj.user +0 -6
- data/tracks/kotlin/exercises/change/src/example/kotlin/Change.kt +0 -70
- data/tracks/kotlin/exercises/change/src/test/kotlin/ChangeTest.kt +0 -55
- data/tracks/scala/exercises/ocr-numbers/src/test/scala/OcrTest.scala +0 -138
@@ -0,0 +1,16 @@
|
|
1
|
+
import java.io.File
|
2
|
+
|
3
|
+
import testgen.TestSuiteBuilder._
|
4
|
+
import testgen._
|
5
|
+
|
6
|
+
object PigLatinTestGenerator {
|
7
|
+
def main(args: Array[String]): Unit = {
|
8
|
+
val file = new File("src/main/resources/pig-latin.json")
|
9
|
+
|
10
|
+
val code =
|
11
|
+
TestSuiteBuilder.build(file, fromLabeledTest("input"))
|
12
|
+
println(s"-------------")
|
13
|
+
println(code)
|
14
|
+
println(s"-------------")
|
15
|
+
}
|
16
|
+
}
|
@@ -0,0 +1,277 @@
|
|
1
|
+
#!/usr/bin/env python
|
2
|
+
|
3
|
+
import json
|
4
|
+
import re
|
5
|
+
import shutil
|
6
|
+
|
7
|
+
from pathlib import Path
|
8
|
+
from collections import OrderedDict
|
9
|
+
from urllib.request import urlopen, HTTPError
|
10
|
+
from http import HTTPStatus
|
11
|
+
|
12
|
+
|
13
|
+
canonical_data_url = 'https://raw.githubusercontent.com/exercism' + \
|
14
|
+
'/problem-specifications/master' + \
|
15
|
+
'/exercises/%s/canonical-data.json'
|
16
|
+
|
17
|
+
|
18
|
+
def fetch(exercise):
|
19
|
+
try:
|
20
|
+
with urlopen(canonical_data_url % exercise) as response:
|
21
|
+
return json.loads(response.read().decode('utf8'))
|
22
|
+
except HTTPError as e:
|
23
|
+
if e.status == HTTPStatus.NOT_FOUND.value:
|
24
|
+
msg = "Exercise '%s' doesn't exist." % exercise
|
25
|
+
else:
|
26
|
+
msg = 'Unexpected error, please try again later.'
|
27
|
+
raise Exception(msg)
|
28
|
+
|
29
|
+
|
30
|
+
def load(exercise):
|
31
|
+
root = Path(__file__).absolute().parent.parent.parent
|
32
|
+
path = root / 'problem-specifications/exercises'
|
33
|
+
with (path / exercise / 'canonical-data.json').open() as f:
|
34
|
+
return json.loads(f.read())
|
35
|
+
|
36
|
+
|
37
|
+
def indent(n, s):
|
38
|
+
return s.rjust(len(s) + n, ' ')
|
39
|
+
|
40
|
+
|
41
|
+
def camelize(s):
|
42
|
+
return re.sub('[-_](\w)', lambda x: x.group(1).upper(), s)
|
43
|
+
|
44
|
+
|
45
|
+
def sml_type(value):
|
46
|
+
if value is None:
|
47
|
+
return 'option'
|
48
|
+
if isinstance(value, list):
|
49
|
+
if not value:
|
50
|
+
return "'a list"
|
51
|
+
s = {sml_type(v)
|
52
|
+
for v in value
|
53
|
+
# }
|
54
|
+
if not (isinstance(v, list) and not v)}
|
55
|
+
s.discard('exn')
|
56
|
+
if not s or len(s) > 2:
|
57
|
+
raise Exception('Failed to infer a proper type')
|
58
|
+
if len(s) == 1:
|
59
|
+
return '%s list' % s.pop()
|
60
|
+
if 'option' not in s:
|
61
|
+
raise Exception('Failed to infer a proper type, got multiple types', value, s)
|
62
|
+
s.remove('option')
|
63
|
+
return '%s option list' % s.pop()
|
64
|
+
if isinstance(value, dict):
|
65
|
+
if 'error' in value:
|
66
|
+
return 'exn'
|
67
|
+
return '{%s}' % ', '.join(
|
68
|
+
'%s: %s' % (key, sml_type(value[key]))
|
69
|
+
for key in sorted(value.keys())
|
70
|
+
)
|
71
|
+
types = {int: 'int', str: 'string', float: 'real', bool: 'bool'}
|
72
|
+
return types[type(value)]
|
73
|
+
|
74
|
+
|
75
|
+
def sml_value(value, smltype=''):
|
76
|
+
if value is None:
|
77
|
+
return 'NONE'
|
78
|
+
_value = None
|
79
|
+
if isinstance(value, str):
|
80
|
+
_value = json.dumps(value)
|
81
|
+
if isinstance(value, bool):
|
82
|
+
_value = str(value).lower()
|
83
|
+
if isinstance(value, (int, float)):
|
84
|
+
if value < 0:
|
85
|
+
_value = '~%s' % -value
|
86
|
+
else:
|
87
|
+
_value = str(value)
|
88
|
+
if isinstance(value, list):
|
89
|
+
_value = '[%s]' % ', '.join(sml_value(v) for v in value)
|
90
|
+
if isinstance(value, dict):
|
91
|
+
if 'error' in value:
|
92
|
+
return 'Fail "%s"' % value['error']
|
93
|
+
_value = '{%s}' % ', '.join('%s = %s' % (k, sml_value(v))
|
94
|
+
for k, v in value.items())
|
95
|
+
return _value if 'option' not in smltype else ('SOME ' + _value)
|
96
|
+
|
97
|
+
|
98
|
+
def get_fn_args(case):
|
99
|
+
ignored_keys = {'property', 'expected', 'description', 'comments'}
|
100
|
+
return OrderedDict(
|
101
|
+
(k, case[k])
|
102
|
+
for k in sorted(case.keys())
|
103
|
+
if k not in ignored_keys
|
104
|
+
)
|
105
|
+
|
106
|
+
|
107
|
+
def extract_signatures(data):
|
108
|
+
funcs = OrderedDict()
|
109
|
+
exercise = data['exercise']
|
110
|
+
ignored_keys = {'property', 'expected', 'description', 'comments'}
|
111
|
+
|
112
|
+
def type_aux(values):
|
113
|
+
s = {sml_type(val) for val in values}
|
114
|
+
if len(s) == 1:
|
115
|
+
return s.pop()
|
116
|
+
if len(s) == 2 and 'option' in s:
|
117
|
+
s.remove('option')
|
118
|
+
return '%s option' % s.pop()
|
119
|
+
raise Exception('Failed to infer a proper type, can not unify different types: %s' % s)
|
120
|
+
|
121
|
+
def signature(xs):
|
122
|
+
output = []
|
123
|
+
args = OrderedDict()
|
124
|
+
for x in xs:
|
125
|
+
output.append(x['output'])
|
126
|
+
for arg, val in x['args'].items():
|
127
|
+
args.setdefault(arg, []).append(val)
|
128
|
+
return {
|
129
|
+
'args': OrderedDict((arg, type_aux(vals)) for arg, vals in args.items()),
|
130
|
+
'output': type_aux(output),
|
131
|
+
}
|
132
|
+
|
133
|
+
def collect(cases):
|
134
|
+
for case in cases:
|
135
|
+
if 'cases' in case:
|
136
|
+
collect(case['cases'])
|
137
|
+
else:
|
138
|
+
# assumes there's at least one test with non "nullish" vars
|
139
|
+
fn = camelize(case.get('property', exercise))
|
140
|
+
if sml_type(case['expected']) == 'exn':
|
141
|
+
continue
|
142
|
+
args = get_fn_args(case)
|
143
|
+
if not all(args.values()) or sml_type(case['expected']) == "'a list":# or not case['expected']:
|
144
|
+
continue
|
145
|
+
funcs.setdefault(fn, []).append({
|
146
|
+
'args': args,
|
147
|
+
'output': case['expected']
|
148
|
+
})
|
149
|
+
|
150
|
+
collect(data['cases'])
|
151
|
+
return {fn: signature(funcs[fn]) for fn in funcs}
|
152
|
+
|
153
|
+
|
154
|
+
def expectation(signature, fn, args, expected):
|
155
|
+
tmpl = '(fn _ => %s |> Expect.%s)'
|
156
|
+
output = sml_value(expected, signature['output'])
|
157
|
+
invocation = '%s (%s)' % (
|
158
|
+
fn,
|
159
|
+
', '.join((
|
160
|
+
sml_value(val, signature['args'][arg])
|
161
|
+
for arg, val in args.items())
|
162
|
+
)
|
163
|
+
)
|
164
|
+
if sml_type(expected) == 'exn':
|
165
|
+
return tmpl % (
|
166
|
+
'(fn _ => %s)' % invocation,
|
167
|
+
'error (%s)' % output
|
168
|
+
)
|
169
|
+
if signature['output'] == 'bool':
|
170
|
+
return tmpl % (
|
171
|
+
invocation,
|
172
|
+
'truthy' if expected else 'falsy'
|
173
|
+
)
|
174
|
+
if signature['output'] == 'real':
|
175
|
+
return tmpl % (
|
176
|
+
invocation,
|
177
|
+
'nearTo %s' % output
|
178
|
+
)
|
179
|
+
return tmpl % (
|
180
|
+
invocation,
|
181
|
+
('equalTo (%s)' if 'SOME ' in output else 'equalTo %s') % output
|
182
|
+
)
|
183
|
+
|
184
|
+
|
185
|
+
def generate_test_content(data, signature):
|
186
|
+
exercise = data['exercise']
|
187
|
+
|
188
|
+
def fmt(case, depth=0):
|
189
|
+
if 'property' not in case:
|
190
|
+
print('WARNING: property not found using exercise name as function name')
|
191
|
+
fn = camelize(case.get('property', exercise))
|
192
|
+
expected = case['expected']
|
193
|
+
args = get_fn_args(case)
|
194
|
+
return '\n'.join((
|
195
|
+
indent(depth, 'test "%s"' % case.get('description', fn)),
|
196
|
+
indent(depth + 2, expectation(signature[fn], fn, args, expected))
|
197
|
+
))
|
198
|
+
|
199
|
+
def traverse(cases, depth=0):
|
200
|
+
acc = []
|
201
|
+
for case in cases:
|
202
|
+
if 'cases' in case:
|
203
|
+
acc.append('\n'.join((
|
204
|
+
indent(depth * 2, 'describe "%s" [' % case['description']),
|
205
|
+
',\n\n'.join(traverse(case['cases'], depth + 1)),
|
206
|
+
indent(depth * 2, ']')
|
207
|
+
)))
|
208
|
+
else:
|
209
|
+
acc.append(fmt(case, depth * 2))
|
210
|
+
return acc
|
211
|
+
|
212
|
+
return '\n'.join([
|
213
|
+
'(* version %s *)' % data['version'],
|
214
|
+
'',
|
215
|
+
'use "%s.sml";' % data['exercise'],
|
216
|
+
'use "testlib.sml";',
|
217
|
+
'',
|
218
|
+
'infixr |>',
|
219
|
+
'fun x |> f = f x',
|
220
|
+
'',
|
221
|
+
'val testsuite =',
|
222
|
+
' describe "%s" [' % data['exercise'],
|
223
|
+
',\n\n'.join(traverse(data['cases'], depth=2)),
|
224
|
+
' ]',
|
225
|
+
'',
|
226
|
+
'val _ = Test.run testsuite'
|
227
|
+
])
|
228
|
+
|
229
|
+
|
230
|
+
def generate_exercise_content(signatures):
|
231
|
+
def fmt(args):
|
232
|
+
return ', '.join('%s: %s' % (name, value)
|
233
|
+
for name, value in args.items())
|
234
|
+
|
235
|
+
return '\n\n'.join(
|
236
|
+
'fun {0} ({1}): {2} =\n'
|
237
|
+
' raise Fail "\'{0}\' is not implemented"'.format(
|
238
|
+
fn,
|
239
|
+
fmt(signature['args']),
|
240
|
+
signature['output']
|
241
|
+
)
|
242
|
+
for fn, signature in signatures.items()
|
243
|
+
)
|
244
|
+
|
245
|
+
|
246
|
+
def write(path, content):
|
247
|
+
with path.open('w') as f:
|
248
|
+
f.write(content)
|
249
|
+
|
250
|
+
|
251
|
+
def generate(exercise):
|
252
|
+
root = Path(__file__).parent.parent.absolute()
|
253
|
+
path = root / 'exercises' / exercise
|
254
|
+
if not path.exists():
|
255
|
+
path.mkdir()
|
256
|
+
data = fetch(exercise)
|
257
|
+
signatures = extract_signatures(data)
|
258
|
+
write(path / 'test.sml', generate_test_content(data, signatures))
|
259
|
+
content = generate_exercise_content(signatures)
|
260
|
+
write(path / ('%s.sml' % exercise), content)
|
261
|
+
write(path / 'example.sml', content)
|
262
|
+
shutil.copyfile(
|
263
|
+
(root / 'lib/testlib.sml').as_posix(),
|
264
|
+
(path / 'testlib.sml').as_posix()
|
265
|
+
)
|
266
|
+
|
267
|
+
|
268
|
+
if __name__ == '__main__':
|
269
|
+
import sys
|
270
|
+
|
271
|
+
for exercise in sys.argv[1:]:
|
272
|
+
try:
|
273
|
+
generate(exercise)
|
274
|
+
except FileNotFoundError:
|
275
|
+
print('[%s]: canonical-data.json not found' % exercise)
|
276
|
+
except Exception as e:
|
277
|
+
print('[%s]: %s' % (exercise, e))
|
data/tracks/sml/config.json
CHANGED
@@ -4,6 +4,26 @@
|
|
4
4
|
"active": false,
|
5
5
|
"test_pattern": "test[.]sml$",
|
6
6
|
"exercises": [
|
7
|
+
{
|
8
|
+
"uuid": "4450455d-0786-6f80-7672-48368a115df16e0aa7e",
|
9
|
+
"slug": "hello-world",
|
10
|
+
"core": true,
|
11
|
+
"unlocked_by": null,
|
12
|
+
"difficulty": 1,
|
13
|
+
"topics": [
|
14
|
+
|
15
|
+
]
|
16
|
+
},
|
17
|
+
{
|
18
|
+
"uuid": "9d8e76f1-0e85-a280-b779-16335f29d1a96ce3a72",
|
19
|
+
"slug": "bob",
|
20
|
+
"core": false,
|
21
|
+
"unlocked_by": null,
|
22
|
+
"difficulty": 1,
|
23
|
+
"topics": [
|
24
|
+
|
25
|
+
]
|
26
|
+
},
|
7
27
|
{
|
8
28
|
"uuid": "333c2cb9-f03c-473c-9eeb-096072a321b2",
|
9
29
|
"slug": "accumulate",
|
@@ -74,6 +94,16 @@
|
|
74
94
|
|
75
95
|
]
|
76
96
|
},
|
97
|
+
{
|
98
|
+
"uuid": "b6713a74-08d5-4480-6524-f9dbf5cf2563d2b2a91",
|
99
|
+
"slug": "difference-of-squares",
|
100
|
+
"core": false,
|
101
|
+
"unlocked_by": null,
|
102
|
+
"difficulty": 1,
|
103
|
+
"topics": [
|
104
|
+
|
105
|
+
]
|
106
|
+
},
|
77
107
|
{
|
78
108
|
"uuid": "225cfd7d-81a3-4a58-b1aa-1de2e40e7a93",
|
79
109
|
"slug": "binary",
|
@@ -0,0 +1,32 @@
|
|
1
|
+
[Standard ML (SML)](https://en.wikipedia.org/wiki/Standard_ml) is one of the two main dialects of the ML programming
|
2
|
+
language. [ML](https://en.wikipedia.org/wiki/ML_programming_language) was the first strong statically typed language,
|
3
|
+
developed in the early 1970s at the University of Edinburgh.
|
4
|
+
|
5
|
+
Despite it's age, SML feels very young in many ways; SML had features that mainstream languages would not pick up for decades and are still being experimented with today.
|
6
|
+
|
7
|
+
Here are some of ML's "cutting-edge" features:
|
8
|
+
|
9
|
+
- [**strong** static typing](https://en.wikipedia.org/wiki/Type_system#Static_typing)
|
10
|
+
- [automatic type inference](https://en.wikipedia.org/wiki/Type_inference)
|
11
|
+
- exception handling
|
12
|
+
- [pattern matching](https://en.wikipedia.org/wiki/Pattern_matching)
|
13
|
+
- parametric polymorphism
|
14
|
+
- [first class functions](https://en.wikipedia.org/wiki/First-class_function)
|
15
|
+
|
16
|
+
SML was originally designed for developping proofs about first-order predicate calculus (read: _computer programs_)
|
17
|
+
and it can have a distinct academic feel about it.
|
18
|
+
|
19
|
+
However it's emphasis on immutability and strong typing has led SML to be used in many fields where program correctness is paramount (compiler design, code analysis, financial systems, medical systems, etc...).
|
20
|
+
|
21
|
+
Learning SML makes you a better programmer, because it forces you to write code that is stateless and to use closures
|
22
|
+
effectively.
|
23
|
+
|
24
|
+
It's also many programmers first introduction to pattern matching and (truely) strong typing. And because SML's type system is so strong and well-thought out, it often feels like you are working in a dynamically typed language instead.
|
25
|
+
|
26
|
+
There are several popular implementations:
|
27
|
+
|
28
|
+
- [MLton](http://mlton.org/)
|
29
|
+
- [SML/NJ](http://www.smlnj.org/)
|
30
|
+
- [PolyML](http://www.polyml.org/).
|
31
|
+
|
32
|
+
You can find information on the language on each implementation's sites.
|
@@ -0,0 +1,37 @@
|
|
1
|
+
## Recommended Learning Resources
|
2
|
+
|
3
|
+
Exercism provides exercises and feedback but ML can be difficult to jump into for those learning SML for the first time.
|
4
|
+
These resources can help you get started:
|
5
|
+
|
6
|
+
### I've never programmed before
|
7
|
+
|
8
|
+
ML is probably not the best language for new programmers. You are probably better off starting with a language like
|
9
|
+
Ruby or Python, but if you are feeling particularly bold you might want to read [Programming in Standard ML '97:
|
10
|
+
An On-line Tutorial](https://www.cs.cmu.edu/~rwh/introsml/) I suggest skipping the introduction and jumping straight
|
11
|
+
to [Simple applicative programming ](http://homepages.inf.ed.ac.uk/stg/NOTES/node13.html), because the introduction
|
12
|
+
is very technical.
|
13
|
+
|
14
|
+
Coursera offers a course on [programming languages](https://www.coursera.org/learn/programming-languages).
|
15
|
+
The course is aimed at somewhat experienced programmers, but it's hard to find a better introduction to the ML language.
|
16
|
+
|
17
|
+
|
18
|
+
### I've heard of functional programming
|
19
|
+
|
20
|
+
A good read is [Programming in Standard ML '97: An On-line Tutorial](https://www.cs.cmu.edu/~rwh/introsml/).
|
21
|
+
|
22
|
+
Another is [A Gentle Introduction to ML](http://www.soc.napier.ac.uk/course-notes/sml/manual.html).
|
23
|
+
|
24
|
+
Coursera offers a course on [programming languages](https://www.coursera.org/learn/programming-languages).
|
25
|
+
It's hard to find a better introduction to the ML language.
|
26
|
+
|
27
|
+
The [SML/NJ site](http://smlnj.org/doc/literature.html) lists a number of books. Most are quite old and are probably hard to find though.
|
28
|
+
The language hasn't changed much so if you can find them, don't think that they are that out of date. Also check the
|
29
|
+
Four Lectures on Standard ML listed on the page.
|
30
|
+
|
31
|
+
|
32
|
+
## I am a confident programmer
|
33
|
+
|
34
|
+
If you are a confident programmer and if you are very familiar with functional languages, than you can get a good overview of the
|
35
|
+
syntax at [Learn X in Y minutes](https://learnxinyminutes.com/docs/standard-ml/)
|
36
|
+
|
37
|
+
The [Four Lectures on Standard ML](http://smlnj.org/doc/literature.html) are a good introduction.
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# Bob
|
2
|
+
|
3
|
+
Bob is a lackadaisical teenager. In conversation, his responses are very limited.
|
4
|
+
|
5
|
+
Bob answers 'Sure.' if you ask him a question.
|
6
|
+
|
7
|
+
He answers 'Whoa, chill out!' if you yell at him.
|
8
|
+
|
9
|
+
He says 'Fine. Be that way!' if you address him without actually saying
|
10
|
+
anything.
|
11
|
+
|
12
|
+
He answers 'Whatever.' to anything else.
|
13
|
+
|
14
|
+
## Loading your exercise implementation in PolyML
|
15
|
+
|
16
|
+
```
|
17
|
+
$ poly --use {exercise}.sml
|
18
|
+
```
|
19
|
+
|
20
|
+
Or:
|
21
|
+
|
22
|
+
```
|
23
|
+
$ poly
|
24
|
+
> use "{exercise}.sml";
|
25
|
+
```
|
26
|
+
|
27
|
+
**Note:** You have to replace {exercise}.
|
28
|
+
|
29
|
+
## Running the tests
|
30
|
+
|
31
|
+
```
|
32
|
+
$ poly -q --use test.sml
|
33
|
+
```
|
34
|
+
|
35
|
+
## Feedback, Issues, Pull Requests
|
36
|
+
|
37
|
+
The [exercism/sml](https://github.com/exercism/sml) repository on
|
38
|
+
GitHub is the home for all of the Standard ML exercises.
|
39
|
+
|
40
|
+
If you have feedback about an exercise, or want to help implementing a new
|
41
|
+
one, head over there and create an issue. We'll do our best to help you!
|
42
|
+
|
43
|
+
## Source
|
44
|
+
|
45
|
+
Inspired by the 'Deaf Grandma' exercise in Chris Pine's Learn to Program tutorial. [http://pine.fm/LearnToProgram/?Chapter=06](http://pine.fm/LearnToProgram/?Chapter=06)
|
46
|
+
|
47
|
+
## Submitting Incomplete Solutions
|
48
|
+
It's possible to submit an incomplete solution so you can see how others have completed the exercise.
|