rubymurray 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +20 -0
- data/README +35 -0
- data/Rakefile +379 -0
- data/doc/currybook.rdoc +146 -0
- data/doc/jamis.rb +591 -0
- data/lib/curry.rb +291 -0
- data/setup.rb +35 -0
- data/test/tc_curry.rb +170 -0
- metadata +61 -0
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c)2006 Ross Bamford. All rights reserved.
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
4
|
+
this software and associated documentation files (the "Software"), to deal in
|
5
|
+
the Software without restriction, including without limitation the rights to
|
6
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
7
|
+
of the Software, and to permit persons to whom the Software is furnished to do
|
8
|
+
so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
11
|
+
copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
19
|
+
SOFTWARE.
|
20
|
+
|
data/README
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
= Ruby Murray - Ruby version of Perl's Sub::Curry.
|
2
|
+
== (c)2006 Ross Bamford
|
3
|
+
|
4
|
+
Originally for Ruby Quiz 64. License: see +LICENSE+.
|
5
|
+
See +Curry+ for documentation and the currybook for examples.
|
6
|
+
|
7
|
+
You can download Ruby Murray from the Project Homepage on Rubyforge.
|
8
|
+
|
9
|
+
== Install
|
10
|
+
|
11
|
+
=== As a Gem:
|
12
|
+
|
13
|
+
gem install rubymurray
|
14
|
+
|
15
|
+
=== Manual (site-ruby)
|
16
|
+
|
17
|
+
ruby setup.rb
|
18
|
+
|
19
|
+
or
|
20
|
+
|
21
|
+
rake install
|
22
|
+
|
23
|
+
== Usage
|
24
|
+
|
25
|
+
See currybook.rdoc for examples and ideas. See also the unit-tests
|
26
|
+
included with the distribution.
|
27
|
+
|
28
|
+
== Changelog
|
29
|
+
|
30
|
+
0.1.0 - 2005/01/27 - First version (Ruby Quiz #64)
|
31
|
+
0.1.2 - 2005/02/02 - Changed call_spice behaviour when processing
|
32
|
+
leftover SpiceArgs, arg is now called with
|
33
|
+
no remaining args.
|
34
|
+
|
35
|
+
Released on RubyForge.
|
data/Rakefile
ADDED
@@ -0,0 +1,379 @@
|
|
1
|
+
require 'rake/testtask'
|
2
|
+
require 'rake/rdoctask'
|
3
|
+
require 'rake/gempackagetask'
|
4
|
+
|
5
|
+
SRC_RB = FileList['lib/**/*.rb']
|
6
|
+
|
7
|
+
# Determine the current version of the software
|
8
|
+
if File.read('lib/curry.rb') =~ /\s*VERSION\s*=\s*['"](\d.+)['"]/
|
9
|
+
CURRENT_VERSION = $1
|
10
|
+
else
|
11
|
+
CURRENT_VERSION = "0.0.0"
|
12
|
+
end
|
13
|
+
|
14
|
+
if ENV['REL']
|
15
|
+
PKG_VERSION = ENV['REL']
|
16
|
+
else
|
17
|
+
PKG_VERSION = CURRENT_VERSION
|
18
|
+
end
|
19
|
+
|
20
|
+
# The default task is run if rake is given no explicit arguments.
|
21
|
+
|
22
|
+
desc "Default Task (All tests)"
|
23
|
+
task :default => :alltests
|
24
|
+
|
25
|
+
# Test Tasks ---------------------------------------------------------
|
26
|
+
|
27
|
+
task :ta => :alltests
|
28
|
+
task :tu => :unittests
|
29
|
+
task :test => :unittests
|
30
|
+
|
31
|
+
Rake::TestTask.new(:alltests) do |t|
|
32
|
+
t.test_files = FileList[
|
33
|
+
'test/tc*.rb',
|
34
|
+
'test/contrib/tc*.rb',
|
35
|
+
'test/fun*.rb'
|
36
|
+
]
|
37
|
+
t.warning = true
|
38
|
+
t.verbose = true
|
39
|
+
end
|
40
|
+
|
41
|
+
Rake::TestTask.new(:unittests) do |t|
|
42
|
+
t.test_files = FileList['test/tc_*.rb']
|
43
|
+
t.warning = true
|
44
|
+
t.verbose = false
|
45
|
+
end
|
46
|
+
|
47
|
+
# Install using the standard setup.rb script.
|
48
|
+
desc "Install to systemwide site-ruby directory"
|
49
|
+
task :install do
|
50
|
+
ruby "setup.rb"
|
51
|
+
end
|
52
|
+
|
53
|
+
task :upload => :doc do
|
54
|
+
Dir.chdir('doc') do
|
55
|
+
Rake::FtpUploader.connect('/htdocs/roscopeco/code/ruby-quiz-entries/64','roscopeco.co.uk','synergix','daytona') do |ftp|
|
56
|
+
ftp.instance_eval { @ftp.passive = true }
|
57
|
+
ftp.verbose = true
|
58
|
+
ftp.upload_files('**/*')
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# Website / Doc tasks ------------------------------------------------
|
64
|
+
|
65
|
+
desc "Build the Rdoc documentation"
|
66
|
+
rd = Rake::RDocTask.new("doc") { |rdoc|
|
67
|
+
rdoc.rdoc_dir = 'html'
|
68
|
+
rdoc.template = 'doc/jamis.rb'
|
69
|
+
rdoc.title = "Ruby Murray"
|
70
|
+
rdoc.options << '--line-numbers' << '--inline-source' << '--main' << 'README'
|
71
|
+
rdoc.rdoc_files.include('README', 'LICENSE')
|
72
|
+
rdoc.rdoc_files.include('lib/**/*.rb', 'doc/**/*.rdoc')
|
73
|
+
rdoc.rdoc_files.exclude(/\bcontrib\b/)
|
74
|
+
}
|
75
|
+
|
76
|
+
desc "Publish the documentation and web site"
|
77
|
+
task :doc_upload => [ :doc ] do
|
78
|
+
if acct = ENV['RUBYFORGE_ACCT']
|
79
|
+
require 'rake/contrib/sshpublisher'
|
80
|
+
Rake::SshDirPublisher.new(
|
81
|
+
"#{acct}@rubyforge.org",
|
82
|
+
"/var/www/gforge-projects/rote",
|
83
|
+
"html"
|
84
|
+
).upload
|
85
|
+
else
|
86
|
+
$stderr << "Skipping documentation upload - Need to set RUBYFORGE_ACCT to your rubyforge.org user name"
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# ====================================================================
|
91
|
+
# Create a task that will package the software into distributable
|
92
|
+
# tar, zip and gem files.
|
93
|
+
|
94
|
+
PKG_FILES = FileList[
|
95
|
+
'setup.rb',
|
96
|
+
'[A-Z]*',
|
97
|
+
'lib/**/*.rb',
|
98
|
+
'test/**/*',
|
99
|
+
'doc/**/*'
|
100
|
+
]
|
101
|
+
|
102
|
+
if ! defined?(Gem)
|
103
|
+
puts "Package Target requires RubyGEMs"
|
104
|
+
else
|
105
|
+
spec = Gem::Specification.new do |s|
|
106
|
+
|
107
|
+
#### Basic information.
|
108
|
+
|
109
|
+
s.name = 'rubymurray'
|
110
|
+
s.version = PKG_VERSION
|
111
|
+
s.summary = "A Ruby port of Perl's Sub::Curry"
|
112
|
+
s.description = <<-EOF
|
113
|
+
Ruby Murray is a Ruby port of Perl's Sub::Curry,
|
114
|
+
originally written for Ruby Quiz #64 (Port a library).
|
115
|
+
EOF
|
116
|
+
|
117
|
+
#### Which files are to be included in this gem?
|
118
|
+
|
119
|
+
s.files = PKG_FILES.to_a
|
120
|
+
|
121
|
+
#### Load-time details
|
122
|
+
s.require_path = 'lib' # Use these for libraries.
|
123
|
+
|
124
|
+
#### Documentation and testing.
|
125
|
+
s.has_rdoc = true
|
126
|
+
s.extra_rdoc_files = rd.rdoc_files.reject { |fn| fn =~ /\.rb$/ }.to_a
|
127
|
+
s.rdoc_options <<
|
128
|
+
'--title' << 'Ruby Murray' <<
|
129
|
+
'--main' << 'README' <<
|
130
|
+
'--line-numbers' <<
|
131
|
+
'--inline-source' <<
|
132
|
+
'--template' << 'doc/jamis.rb'
|
133
|
+
'-o' << 'html'
|
134
|
+
|
135
|
+
s.test_files = Dir.glob('test/gem_*.rb')
|
136
|
+
|
137
|
+
#### Author and project details.
|
138
|
+
|
139
|
+
s.author = "Ross Bamford"
|
140
|
+
s.email = "ross@roscopeco.co.uk"
|
141
|
+
s.homepage = "http://rubymurray.rubyforge.org"
|
142
|
+
s.rubyforge_project = "rubymurray"
|
143
|
+
end
|
144
|
+
|
145
|
+
# Quick fix for Ruby 1.8.3 / YAML bug
|
146
|
+
if (RUBY_VERSION == '1.8.3')
|
147
|
+
def spec.to_yaml
|
148
|
+
out = super
|
149
|
+
out = '--- ' + out unless out =~ /^---/
|
150
|
+
out
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
package_task = Rake::GemPackageTask.new(spec) do |pkg|
|
155
|
+
pkg.need_zip = true
|
156
|
+
pkg.need_tar_gz = true
|
157
|
+
pkg.package_dir = 'pkg'
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
# Misc tasks =========================================================
|
162
|
+
|
163
|
+
def count_lines(filename)
|
164
|
+
lines = 0
|
165
|
+
codelines = 0
|
166
|
+
open(filename) { |f|
|
167
|
+
f.each do |line|
|
168
|
+
lines += 1
|
169
|
+
next if line =~ /^\s*$/
|
170
|
+
next if line =~ /^\s*#/
|
171
|
+
codelines += 1
|
172
|
+
end
|
173
|
+
}
|
174
|
+
[lines, codelines]
|
175
|
+
end
|
176
|
+
|
177
|
+
def show_line(msg, lines, loc)
|
178
|
+
printf "%6s %6s %s\n", lines.to_s, loc.to_s, msg
|
179
|
+
end
|
180
|
+
|
181
|
+
desc "Count total lines in source"
|
182
|
+
task :lines do
|
183
|
+
total_lines = 0
|
184
|
+
total_code = 0
|
185
|
+
show_line("File Name", "LINES", "LOC")
|
186
|
+
SRC_RB.each do |fn|
|
187
|
+
lines, codelines = count_lines(fn)
|
188
|
+
show_line(fn, lines, codelines)
|
189
|
+
total_lines += lines
|
190
|
+
total_code += codelines
|
191
|
+
end
|
192
|
+
show_line("TOTAL", total_lines, total_code)
|
193
|
+
end
|
194
|
+
|
195
|
+
ARCHIVEDIR = '/mnt/usb'
|
196
|
+
|
197
|
+
task :archive => [:package] do
|
198
|
+
cp FileList["pkg/*.tar.gz", "pkg/*.zip", "pkg/*.gem"], ARCHIVEDIR
|
199
|
+
end
|
200
|
+
|
201
|
+
# Support Tasks ------------------------------------------------------
|
202
|
+
|
203
|
+
desc "Look for TODO and FIXME tags in the code"
|
204
|
+
task :todo do
|
205
|
+
FileList['**/*.rb'].egrep /#.*(FIXME|TODO|TBD)/
|
206
|
+
end
|
207
|
+
|
208
|
+
desc "Look for Debugging print lines"
|
209
|
+
task :dbg do
|
210
|
+
FileList['**/*.rb'].egrep /\bDBG|\bbreakpoint\b/
|
211
|
+
end
|
212
|
+
|
213
|
+
desc "List all ruby files"
|
214
|
+
task :rubyfiles do
|
215
|
+
puts Dir['**/*.rb'].reject { |fn| fn =~ /^pkg/ }
|
216
|
+
puts Dir['bin/*'].reject { |fn| fn =~ /CVS|(~$)|(\.rb$)/ }
|
217
|
+
end
|
218
|
+
|
219
|
+
desc "Show deprecation notes"
|
220
|
+
task :deprecated do
|
221
|
+
Dir['lib/**/*.r?'].each do |fn|
|
222
|
+
File.open(fn) do |f|
|
223
|
+
[*f].each_with_index do |line,i|
|
224
|
+
if line =~ /#(.*)vv([0-9\.]+)(?:[\s\t]*)v-([0-9\.]+)/
|
225
|
+
cmnt = $~[1].strip
|
226
|
+
cmnt = '<No comment>' if (cmnt.nil? or cmnt.empty?)
|
227
|
+
printf "%s:%d\n%-60s %5s %5s\n\n", fn, i+1, cmnt, $~[2].strip, $~[3].strip
|
228
|
+
end #if splits okay
|
229
|
+
end #each_w_idx
|
230
|
+
end #fopen
|
231
|
+
end #dir
|
232
|
+
end
|
233
|
+
|
234
|
+
desc "Find features deprecated at VER"
|
235
|
+
task :deprecated_by do
|
236
|
+
fail "\nYou must specify the version to check (VER=x.y.z)\n\n" unless ver = ENV['VER']
|
237
|
+
Dir['lib/**/*.r?'].each do |fn|
|
238
|
+
File.open(fn) do |f|
|
239
|
+
[*f].each_with_index do |line,i|
|
240
|
+
if line =~ /vv#{ver}/
|
241
|
+
if line =~ /#(.*)vv([0-9\.]+)(?:[\s\t]*)v-([0-9\.]+)/
|
242
|
+
cmnt = $~[1].strip
|
243
|
+
cmnt = '<No comment>' if (cmnt.nil? or cmnt.empty?)
|
244
|
+
printf "%s:%d\n%-60s %5s %5s\n\n", fn, i+1, cmnt, $~[2].strip, $~[3].strip
|
245
|
+
end #if splits okay
|
246
|
+
end #if line is dep remove
|
247
|
+
end #each_w_idx
|
248
|
+
end #fopen
|
249
|
+
end #dir
|
250
|
+
end
|
251
|
+
|
252
|
+
desc "Find deprecated features to be removed by VER"
|
253
|
+
task :deprecated_due do
|
254
|
+
fail "\nYou must specify the version to check (VER=x.y.z)\n\n" unless ver = ENV['VER']
|
255
|
+
Dir['lib/**/*.r?'].each do |fn|
|
256
|
+
File.open(fn) do |f|
|
257
|
+
[*f].each_with_index do |line,i|
|
258
|
+
if line =~ /v-#{ver}/
|
259
|
+
if line =~ /#(.*)vv([0-9\.]+)(?:[\s\t]*)v-([0-9\.]+)/
|
260
|
+
cmnt = $~[1].strip
|
261
|
+
cmnt = '<No comment>' if (cmnt.nil? or cmnt.empty?)
|
262
|
+
printf "%s:%d\n%-60s %5s %5s\n\n", fn, i+1, cmnt, $~[2].strip, $~[3].strip
|
263
|
+
end #if splits okay
|
264
|
+
end #if line is dep remove
|
265
|
+
end #each_w_idx
|
266
|
+
end #fopen
|
267
|
+
end #dir
|
268
|
+
end
|
269
|
+
|
270
|
+
# --------------------------------------------------------------------
|
271
|
+
# Creating a release
|
272
|
+
|
273
|
+
def announce(msg='')
|
274
|
+
STDERR.puts msg
|
275
|
+
end
|
276
|
+
|
277
|
+
desc "Make a new release"
|
278
|
+
task :release => [
|
279
|
+
:prerelease,
|
280
|
+
:clobber,
|
281
|
+
:alltests,
|
282
|
+
:update_version,
|
283
|
+
:package,
|
284
|
+
:tag,
|
285
|
+
:doc_upload] do
|
286
|
+
|
287
|
+
announce
|
288
|
+
announce "**************************************************************"
|
289
|
+
announce "* Release #{PKG_VERSION} Complete."
|
290
|
+
announce "* Packages ready to upload."
|
291
|
+
announce "**************************************************************"
|
292
|
+
announce
|
293
|
+
end
|
294
|
+
|
295
|
+
# Validate that everything is ready to go for a release.
|
296
|
+
task :prerelease do
|
297
|
+
announce
|
298
|
+
announce "**************************************************************"
|
299
|
+
announce "* Making RubyGem Release #{PKG_VERSION}"
|
300
|
+
announce "* (current version #{CURRENT_VERSION})"
|
301
|
+
announce "**************************************************************"
|
302
|
+
announce
|
303
|
+
|
304
|
+
# Is a release number supplied?
|
305
|
+
unless ENV['REL']
|
306
|
+
fail "Usage: rake release REL=x.y.z [REUSE=tag_suffix]"
|
307
|
+
end
|
308
|
+
|
309
|
+
# Is the release different than the current release.
|
310
|
+
# (or is REUSE set?)
|
311
|
+
if PKG_VERSION == CURRENT_VERSION && ! ENV['REUSE']
|
312
|
+
fail "Current version is #{PKG_VERSION}, must specify REUSE=tag_suffix to reuse version"
|
313
|
+
end
|
314
|
+
|
315
|
+
# Are all source files checked in?
|
316
|
+
if ENV['RELTEST']
|
317
|
+
announce "Release Task Testing, skipping checked-in file test"
|
318
|
+
else
|
319
|
+
announce "Checking for unchecked-in files..."
|
320
|
+
data = `svn status`
|
321
|
+
unless data =~ /^$/
|
322
|
+
fail "SVN status is not clean ... do you have unchecked-in files?"
|
323
|
+
end
|
324
|
+
announce "No outstanding checkins found ... OK"
|
325
|
+
end
|
326
|
+
end
|
327
|
+
|
328
|
+
task :update_version => [:prerelease] do
|
329
|
+
if PKG_VERSION == CURRENT_VERSION
|
330
|
+
announce "No version change ... skipping version update"
|
331
|
+
else
|
332
|
+
announce "Updating Curry version to #{PKG_VERSION}"
|
333
|
+
open("lib/curry.rb") do |rakein|
|
334
|
+
open("lib/curry.rb.new", "w") do |rakeout|
|
335
|
+
rakein.each do |line|
|
336
|
+
if line =~ /^\s*VERSION\s*=\s*/
|
337
|
+
rakeout.puts " VERSION = '#{PKG_VERSION}'"
|
338
|
+
else
|
339
|
+
rakeout.puts line
|
340
|
+
end
|
341
|
+
end
|
342
|
+
end
|
343
|
+
end
|
344
|
+
mv "lib/curry.rb.new", "lib/curry.rb"
|
345
|
+
if ENV['RELTEST']
|
346
|
+
announce "Release Task Testing, skipping commiting of new version"
|
347
|
+
else
|
348
|
+
sh %{svn commit -m "Updated to version #{PKG_VERSION}" lib/curry.rb}
|
349
|
+
end
|
350
|
+
end
|
351
|
+
end
|
352
|
+
|
353
|
+
desc "Create a new SVN tag with the latest release number (REL=x.y.z)"
|
354
|
+
task :tag => [:prerelease] do
|
355
|
+
reltag = "REL_#{PKG_VERSION.gsub(/\./, '_')}"
|
356
|
+
reltag << ENV['REUSE'].gsub(/\./, '_') if ENV['REUSE']
|
357
|
+
announce "Tagging CVS with [#{reltag}]"
|
358
|
+
if ENV['RELTEST']
|
359
|
+
announce "Release Task Testing, skipping SVN tagging"
|
360
|
+
else
|
361
|
+
# need to get current base URL
|
362
|
+
s = `svn info`
|
363
|
+
if s =~ /URL:\s*([^\n]*)\n/
|
364
|
+
svnroot = $1
|
365
|
+
if svnroot =~ /^(.*)\/trunk/
|
366
|
+
svnbase = $1
|
367
|
+
sh %{svn cp #{svnroot} #{svnbase}/tags/#{reltag} -m "Release #{PKG_VERSION}"}
|
368
|
+
else
|
369
|
+
fail "Please merge to trunk before making a release"
|
370
|
+
end
|
371
|
+
else
|
372
|
+
fail "Unable to determine repository URL from 'svn info' - is this a working copy?"
|
373
|
+
end
|
374
|
+
end
|
375
|
+
end
|
376
|
+
|
377
|
+
# Require experimental XForge/Metaproject support.
|
378
|
+
# load 'xforge.rf' if File.exist?('xforge.rf')
|
379
|
+
|
data/doc/currybook.rdoc
ADDED
@@ -0,0 +1,146 @@
|
|
1
|
+
== The Ruby Murray Cookbook - Recipes for Ruby's Sub::Curry port.
|
2
|
+
|
3
|
+
This is a translation of the Sub::Curry cookbook to Ruby Murray.
|
4
|
+
See the original at:
|
5
|
+
|
6
|
+
* http://search.cpan.org/~lodin/Sub-Curry-0.8/lib/Sub/Curry/Cookbook.pod
|
7
|
+
|
8
|
+
(c)2006 Ross Bamford. Same license as Ruby.
|
9
|
+
|
10
|
+
In case you're wondering, see http://en.wikipedia.org/wiki/Ruby_Murray.
|
11
|
+
|
12
|
+
=== Right currying
|
13
|
+
|
14
|
+
Special spices make rcurry unnecessary. You can add trailing
|
15
|
+
spice using blackholes.
|
16
|
+
|
17
|
+
c = lambda { |a,b,c| (a + b) / c }.curry(Curry::BLACKHOLE, 2)
|
18
|
+
c.call(5,3)
|
19
|
+
# => 4
|
20
|
+
|
21
|
+
=== Convert func -> method / method -> func / Curry a method
|
22
|
+
|
23
|
+
These are not really applicable to Ruby - the original was working around
|
24
|
+
Perl's broken implementation. But I guess something like:
|
25
|
+
|
26
|
+
def send_func(obj, msg, *args)
|
27
|
+
obj.send(msg,*args)
|
28
|
+
end
|
29
|
+
|
30
|
+
c = method(:send_func).curry("object")
|
31
|
+
|
32
|
+
c.call(:length)
|
33
|
+
# => 6
|
34
|
+
|
35
|
+
c.call(:+, "ive C")
|
36
|
+
# => "objective C"
|
37
|
+
|
38
|
+
And (more useful?):
|
39
|
+
|
40
|
+
c = "string".method(:slice).curry(Curry::HOLE,2)
|
41
|
+
c.call(0)
|
42
|
+
# => "st"
|
43
|
+
c.call(2)
|
44
|
+
# => "ri"
|
45
|
+
|
46
|
+
=== Add trailing spice after a hole in a curried proc
|
47
|
+
|
48
|
+
You have a hole in your curried subroutine but want to add more spices
|
49
|
+
after or fill out holes after your first hole
|
50
|
+
|
51
|
+
Assuming no special spice before the hole, just put a hole in the hole.
|
52
|
+
|
53
|
+
plus = method(:send_func).curry(Curry::HOLE,:+)
|
54
|
+
"#{plus.spice.inspect} -> #{plus.call("object","ive")}"
|
55
|
+
# => "[<HOLE>, :+] -> objective"
|
56
|
+
|
57
|
+
plusion = plus.new(Curry::HOLE, "ion")
|
58
|
+
"#{plusion.spice.inspect} -> #{plusion.call("object")}"
|
59
|
+
# => "[<HOLE>, :+, "ion"] -> objection"
|
60
|
+
|
61
|
+
=== Add trailing spice after a blackhole
|
62
|
+
|
63
|
+
You want to add more spice, but have a blackhole that swallows anything
|
64
|
+
you try to put in there. You want to keep the blackhole, though.
|
65
|
+
|
66
|
+
withtwo = method(:send_func).curry(Curry::BLACKHOLE,2)
|
67
|
+
"#{withtwo.spice.inspect} -> #{withtwo.call([1,2,3,4],:index).inspect}"
|
68
|
+
# => "[<BLACKHOLE>, 2] -> 1"
|
69
|
+
|
70
|
+
Using a blackhole follwed by a whitehole, we can add extra spice at the
|
71
|
+
end:
|
72
|
+
|
73
|
+
twooh = withtwo.new(Curry::BLACKHOLE, Curry::WHITEHOLE, 0)
|
74
|
+
"#{twooh.spice.inspect} -> #{twooh.call([1,2,3,4],:[]=).inspect}"
|
75
|
+
# => "[<BLACKHOLE>, 2, 0] -> 0"
|
76
|
+
|
77
|
+
Or by placing spice between the black and white holes we can place it
|
78
|
+
immediately after the original blackhole.
|
79
|
+
|
80
|
+
ohtwo = withtwo.new(Curry::BLACKHOLE, 0, Curry::WHITEHOLE)
|
81
|
+
"#{ohtwo.spice.inspect} -> #{ohtwo.call([1,2,3,4],:slice).inspect}"
|
82
|
+
# => "[<BLACKHOLE>, 0, 2] -> [1, 2]"
|
83
|
+
|
84
|
+
By giving the blackhole another blackhole, you have two successive
|
85
|
+
blackholes, the first newly added and the second the one that added the
|
86
|
+
new. The whitehole is then removes the second blackhole -- the blackhole
|
87
|
+
doing the inserting. The spice is then added like usual, yet a blackhole
|
88
|
+
exists at the right place.
|
89
|
+
|
90
|
+
=== Append raw spice without processing
|
91
|
+
|
92
|
+
You have a curry that may have special spices in it. You want to add more
|
93
|
+
spices. You do not want the new spice to be processed as an application on
|
94
|
+
the old spice. You just want to append more spice.
|
95
|
+
|
96
|
+
To do this, you'll have to manually get the parts you need and make
|
97
|
+
a new curry, since applying the new spice the usual way results in it
|
98
|
+
being processed.
|
99
|
+
|
100
|
+
sary = lambda { |*args| args }.curry(Curry::HOLE,'Array')
|
101
|
+
"#{sary.spice.inspect} -> #{sary.call('The',2,3).inspect}"
|
102
|
+
# => "[<HOLE>, "Array"] -> ["The", "Array", 2, 3]"
|
103
|
+
|
104
|
+
sary2 = Curry.new(*sary.spice + [Curry::BLACKHOLE, 'The end'], &sary.uncurried)
|
105
|
+
"#{sary2.spice.inspect} -> #{sary2.call('The',2,3).inspect}"
|
106
|
+
# => "[<HOLE>, "Array", <BLACKHOLE>, "The end"] -> ["The", "Array", 2, 3, "The end"]"
|
107
|
+
|
108
|
+
=== Pass curries as blocks to methods
|
109
|
+
|
110
|
+
Curry implements the to_proc protocol so you're away.
|
111
|
+
|
112
|
+
adjsum = lambda { |sum, val, adj| sum + (val * adj) }.curry(Curry::BLACKHOLE, 2)
|
113
|
+
[1,2,3,4,5].inject(0,&adjsum)
|
114
|
+
# => 30
|
115
|
+
|
116
|
+
=== Apply block arguments via spice
|
117
|
+
|
118
|
+
Right now, Curry doesn't treat block arguments in the spice specially, and in
|
119
|
+
particular won't unwrap (with &) a block at the end of your spice. Also, since
|
120
|
+
it's not possible to pass a block to another block, you cannot pass blocks
|
121
|
+
to your curried procs or chain them together.
|
122
|
+
|
123
|
+
You *can* pass blocks to curried methods in a call, but *only* when they're
|
124
|
+
created from bound methods (passed before the spice, or from Method#curry).
|
125
|
+
|
126
|
+
curry = [10,20,30].method(:inject).curry(Curry::HOLE)
|
127
|
+
curry.call(0) { |s,i| s + i }
|
128
|
+
# => 60
|
129
|
+
|
130
|
+
Taking this a step further, you can do some pretty mad stuff:
|
131
|
+
|
132
|
+
mult_sum = lambda { |sum, i, mult| sum + (i * mult) }.curry(Curry::BLACKHOLE, Curry::HOLE)
|
133
|
+
|
134
|
+
double_sum = mult_sum.new(Curry::BLACKHOLE, Curry::WHITEHOLE, 2)
|
135
|
+
triple_sum = mult_sum.new(Curry::BLACKHOLE, Curry::WHITEHOLE, 3)
|
136
|
+
|
137
|
+
curry.call(0, &double_sum)
|
138
|
+
# => 120
|
139
|
+
|
140
|
+
curry.call(0, &triple_sum)
|
141
|
+
# => 180
|
142
|
+
|
143
|
+
(Wow, that's pretty useless ;)) Ruby doesn't allow block arguments to blocks,
|
144
|
+
so a block attached to a curried proc invocation is ignored.
|
145
|
+
|
146
|
+
|