haskell 0.2.3 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +10 -100
- data/lib/haskell.rb +60 -35
- data/lib/haskell/version.rb +1 -1
- data/test/test_haskell.rb +46 -0
- metadata +4 -4
- data/test/test_rubype.rb +0 -107
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ac0c087788b1e471523aed86d8dfd0c42be2503d
|
4
|
+
data.tar.gz: 610eff264b8bdc1903ea519e857a5346f461aad6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 788e9b367f36ec0420f44fa89166768073332e8748aab5acd65f9c1796e1953367a2e9ed9bc5b738e0fdf53f6eb329237205e6a6dd819851f437b92264f7ccd5
|
7
|
+
data.tar.gz: ae3aa09d9fd3af02d5361bccc45f33d27f6e153b5f9a2a2810693493bd18c5fc8a33f4d35ccd5e19f6be4ff179346376f9533753fad70665614055f42081f174
|
data/README.md
CHANGED
@@ -1,107 +1,17 @@
|
|
1
|
-
#
|
2
|
-
|
3
|
-
Matz has mentioned Ruby3.0 with static type at some confluences. But almost all rubyists(include me) are not sure how typed Ruby is.
|
4
|
-
|
5
|
-
But it's worth thinking more. This gem is kind of trial without so much side-effect.
|
1
|
+
# Haskell On Ruby
|
6
2
|
|
7
3
|
```rb
|
8
4
|
require 'haskell'
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
def wrong_sum(x, y)
|
18
|
-
'string'
|
19
|
-
end
|
20
|
-
typesig wrong_sum: [Numeric, Numeric => Numeric]
|
5
|
+
Haskell.compile %{
|
6
|
+
add :: Integer -> Integer -> Integer
|
7
|
+
add x y = x + y
|
8
|
+
result = add 1 2
|
9
|
+
}
|
10
|
+
|
11
|
+
while Haskell.compiling?
|
12
|
+
...
|
21
13
|
end
|
22
14
|
|
23
|
-
|
15
|
+
p Haskell.execute
|
24
16
|
#=> 3
|
25
|
-
|
26
|
-
MyClass.new.sum(1, 'string')
|
27
|
-
#=> ArgumentError: Wrong type of argument, type of "str" should be Numeric
|
28
|
-
|
29
|
-
MyClass.new.wrong_sum(1, 2)
|
30
|
-
#=> TypeError: Expected wrong_sum to return Numeric but got "str" instead
|
31
|
-
|
32
|
-
|
33
|
-
# ex2
|
34
|
-
class People
|
35
|
-
type People >= Any, def marry(people)
|
36
|
-
# Your Ruby code as usual
|
37
|
-
end
|
38
|
-
end
|
39
|
-
typesig marry: [People => Any]
|
40
|
-
|
41
|
-
People.new.marry(People.new)
|
42
|
-
#=> no error
|
43
|
-
|
44
|
-
People.new.marry('non people')
|
45
|
-
#=> ArgumentError: Wrong type of argument, type of "non people" should be People
|
46
17
|
```
|
47
|
-
|
48
|
-
## Feature
|
49
|
-
### Typed method can coexist with non-typed method
|
50
|
-
|
51
|
-
```ruby
|
52
|
-
# It's totally OK!!
|
53
|
-
class MyClass
|
54
|
-
def sum(x, y)
|
55
|
-
x + y
|
56
|
-
end
|
57
|
-
typesig sum: [Numeric, Numeric => Numeric]
|
58
|
-
|
59
|
-
def sum_without_type(x, y)
|
60
|
-
'string'
|
61
|
-
end
|
62
|
-
end
|
63
|
-
```
|
64
|
-
|
65
|
-
### Duck typing
|
66
|
-
|
67
|
-
```ruby
|
68
|
-
|
69
|
-
class MyClass
|
70
|
-
def foo(any_obj)
|
71
|
-
1
|
72
|
-
end
|
73
|
-
typesig sum: [Any => Numeric]
|
74
|
-
end
|
75
|
-
|
76
|
-
# It's totally OK!!
|
77
|
-
MyClass.new.foo(1)
|
78
|
-
# It's totally OK!!
|
79
|
-
MyClass.new.foo('str')
|
80
|
-
```
|
81
|
-
|
82
|
-
## Installation
|
83
|
-
|
84
|
-
gem install haskell or add gem 'haskell' to your Gemfile.
|
85
|
-
|
86
|
-
This gem requires Ruby 2.0.0+.
|
87
|
-
|
88
|
-
### Contributing
|
89
|
-
|
90
|
-
Fork it ( https://github.com/[my-github-username]/haskell/fork )
|
91
|
-
|
92
|
-
Create your feature branch (`git checkout -b my-new-feature`)
|
93
|
-
|
94
|
-
$ bundle install --path vendor/bundle
|
95
|
-
|
96
|
-
Commit your changes (`git commit -am 'Add some feature'`)
|
97
|
-
|
98
|
-
$ bundle exec rake test
|
99
|
-
|
100
|
-
> 5 runs, 39 assertions, 0 failures, 0 errors, 0 skips
|
101
|
-
|
102
|
-
Push to the branch (`git push origin my-new-feature`)
|
103
|
-
|
104
|
-
Create a new Pull Request
|
105
|
-
|
106
|
-
## Credits
|
107
|
-
[@chancancode](https://github.com/chancancode) first brought this to my attention. I've stolen some idea from him.
|
data/lib/haskell.rb
CHANGED
@@ -1,48 +1,73 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
TrueClass.send(:include, Boolean)
|
5
|
-
FalseClass.send(:include, Boolean)
|
6
|
-
|
7
|
-
class Module
|
8
|
-
private
|
9
|
-
def __haskell__
|
10
|
-
prepend (@__haskell__ = Module.new) unless @__haskell__
|
11
|
-
@__haskell__
|
12
|
-
end
|
1
|
+
module Haskell
|
2
|
+
# TODO:
|
3
|
+
class HaskellCompileError < StandardError; end
|
13
4
|
|
14
|
-
|
15
|
-
|
16
|
-
|
5
|
+
class << self
|
6
|
+
def invoke_sandbox!
|
7
|
+
file_path = File.expand_path('../', __FILE__)
|
8
|
+
$sandbox_path = "#{file_path}/haskell_executing_sandbox"
|
9
|
+
FileUtils.mkdir($sandbox_path) unless Dir.exist?($sandbox_path)
|
10
|
+
end
|
17
11
|
|
18
|
-
|
19
|
-
|
20
|
-
rtn = super(*args, &block)
|
21
|
-
::Haskell.assert_trn_type(meth, rtn, type_pair.values.first)
|
22
|
-
rtn
|
12
|
+
def revoke_sandbox!
|
13
|
+
FileUtils.rm_rf($sandbox_path)
|
23
14
|
end
|
24
|
-
self
|
25
|
-
end
|
26
|
-
end
|
27
15
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
16
|
+
def compile(hs_code)
|
17
|
+
FileUtils.touch("#{$sandbox_path}/COMPILING")
|
18
|
+
#TODO: need inform user prefer message
|
19
|
+
Thread.abort_on_exception = true
|
20
|
+
Thread.new do
|
21
|
+
begin
|
22
|
+
executable_code = executable_code(hs_code)
|
23
|
+
puts_notation(executable_code)
|
24
|
+
File.write("#{$sandbox_path}/tmp.hs", executable_code)
|
25
|
+
Kernel.system("ghc #{$sandbox_path}/tmp.hs")
|
26
|
+
raise HaskellCompileError unless compiled?
|
27
|
+
ensure
|
28
|
+
FileUtils.rm("#{$sandbox_path}/COMPILING")
|
34
29
|
end
|
35
30
|
end
|
31
|
+
rescue
|
32
|
+
raise "Something wrong...https://github.com/Azabuhs/Ruby/issues"
|
36
33
|
end
|
37
34
|
|
38
|
-
def
|
39
|
-
|
40
|
-
raise TypeError, "Expected #{meth} to return #{klass} but got #{rtn.inspect} instead"
|
41
|
-
end
|
35
|
+
def compiling?
|
36
|
+
File.exist?("#{$sandbox_path}/COMPILING")
|
42
37
|
end
|
43
38
|
|
44
|
-
def
|
45
|
-
|
39
|
+
def compiled?
|
40
|
+
File.exist?("#{$sandbox_path}/tmp")
|
41
|
+
end
|
42
|
+
|
43
|
+
def execute
|
44
|
+
`#{$sandbox_path}/tmp`.gsub(/\n\z/, '')
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def executable_code(hs_code)
|
50
|
+
# TODO: other white space
|
51
|
+
hs_code =~/\A\n( *)/
|
52
|
+
hs_code.gsub!(/\n#{$1}/, "\n")
|
53
|
+
<<-HASKELL_CODE
|
54
|
+
module Main where
|
55
|
+
#{hs_code}
|
56
|
+
main = do putStrLn $ show result
|
57
|
+
HASKELL_CODE
|
58
|
+
end
|
59
|
+
|
60
|
+
def puts_notation(executable_code)
|
61
|
+
puts <<-NOTATION
|
62
|
+
# GHC will compile below code
|
63
|
+
###############################
|
64
|
+
NOTATION
|
65
|
+
|
66
|
+
puts '# ' + executable_code.gsub("\n", "\n# ")
|
67
|
+
|
68
|
+
puts <<-NOTATION
|
69
|
+
###############################
|
70
|
+
NOTATION
|
46
71
|
end
|
47
72
|
end
|
48
73
|
end
|
data/lib/haskell/version.rb
CHANGED
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'minitest_helper'
|
2
|
+
|
3
|
+
class TestHaskell < MiniTest::Unit::TestCase
|
4
|
+
def test_main
|
5
|
+
assert_equal_execute('3') {"
|
6
|
+
add :: Integer -> Integer -> Integer
|
7
|
+
add x y = x + y
|
8
|
+
result = add 1 2
|
9
|
+
"}
|
10
|
+
|
11
|
+
assert_equal_execute('[3.1415927,4.0]') {"
|
12
|
+
data Point = Pt Float Float deriving (Show)
|
13
|
+
data Shape = Circle Point Float | Rectangle Point Point deriving (Show)
|
14
|
+
surface :: Shape -> Float
|
15
|
+
surface (Circle _ r) | r > 0 = pi * r ^ 2
|
16
|
+
| otherwise = 0
|
17
|
+
|
18
|
+
surface (Rectangle (Pt x1 y1) (Pt x2 y2)) = (abs $ x2 - x1) * (abs $ y2 - y1)
|
19
|
+
|
20
|
+
result = [surface (Circle (Pt 0 0) 1), surface (Rectangle (Pt 2 3) (Pt 4 5))]
|
21
|
+
"}
|
22
|
+
|
23
|
+
assert_raise_compile_error {"
|
24
|
+
Error
|
25
|
+
"}
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
def assert_equal_execute(str, &block)
|
30
|
+
Haskell.invoke_sandbox!
|
31
|
+
Haskell.compile(block.call)
|
32
|
+
nil while Haskell.compiling?
|
33
|
+
assert_equal str, Haskell.execute
|
34
|
+
Haskell.revoke_sandbox!
|
35
|
+
end
|
36
|
+
|
37
|
+
def assert_raise_compile_error(&block)
|
38
|
+
Haskell.invoke_sandbox!
|
39
|
+
Haskell.compile(block.call)
|
40
|
+
nil while Haskell.compiling?
|
41
|
+
# TODO: more prefer test
|
42
|
+
rescue Haskell::HaskellCompileError
|
43
|
+
assert(true)
|
44
|
+
Haskell.revoke_sandbox!
|
45
|
+
end
|
46
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: haskell
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- gogotanaka
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-12-
|
11
|
+
date: 2014-12-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -85,7 +85,7 @@ files:
|
|
85
85
|
- lib/haskell/version.rb
|
86
86
|
- rubype.gemspec
|
87
87
|
- test/minitest_helper.rb
|
88
|
-
- test/
|
88
|
+
- test/test_haskell.rb
|
89
89
|
homepage: http://gogotanaka.me/
|
90
90
|
licenses:
|
91
91
|
- MIT
|
@@ -112,4 +112,4 @@ specification_version: 4
|
|
112
112
|
summary: Ruby with type.
|
113
113
|
test_files:
|
114
114
|
- test/minitest_helper.rb
|
115
|
-
- test/
|
115
|
+
- test/test_haskell.rb
|
data/test/test_rubype.rb
DELETED
@@ -1,107 +0,0 @@
|
|
1
|
-
require 'minitest_helper'
|
2
|
-
class TypePair
|
3
|
-
def to_s
|
4
|
-
"#{last_arg_type} => #{rtn_type}"
|
5
|
-
end
|
6
|
-
end
|
7
|
-
class TestHaskell < MiniTest::Unit::TestCase
|
8
|
-
def setup
|
9
|
-
@string = 'str'
|
10
|
-
@numeric = 1
|
11
|
-
@symbol = :test
|
12
|
-
@array = [1, 2, 3]
|
13
|
-
@hash = { test: :hash }
|
14
|
-
end
|
15
|
-
|
16
|
-
def test_correct_type
|
17
|
-
assert_correct_type [Numeric => Numeric], [@numeric], @numeric
|
18
|
-
assert_correct_type [Numeric => Array ], [@numeric], @array
|
19
|
-
assert_correct_type [Numeric => String ], [@numeric], @string
|
20
|
-
assert_correct_type [Numeric => Hash ], [@numeric], @hash
|
21
|
-
assert_correct_type [Numeric => Symbol ], [@numeric], @symbol
|
22
|
-
assert_correct_type [Numeric => Boolean], [@numeric], true
|
23
|
-
assert_correct_type [Numeric => Boolean], [@numeric], false
|
24
|
-
|
25
|
-
assert_correct_type [Boolean, Numeric => Numeric], [true, @numeric], @numeric
|
26
|
-
assert_correct_type [Boolean, Array => Array ], [true, @array ], @array
|
27
|
-
assert_correct_type [Boolean, String => String ], [true, @string ], @string
|
28
|
-
assert_correct_type [Boolean, Hash => Hash ], [true, @hash ], @hash
|
29
|
-
assert_correct_type [Boolean, Symbol => Symbol ], [true, @symbol ], @symbol
|
30
|
-
end
|
31
|
-
|
32
|
-
def test_wrong_return_type
|
33
|
-
assert_wrong_rtn [Numeric => Numeric], [@numeric], @array
|
34
|
-
assert_wrong_rtn [Numeric => Numeric], [@numeric], @string
|
35
|
-
assert_wrong_rtn [Numeric => Numeric], [@numeric], @hash
|
36
|
-
assert_wrong_rtn [Numeric => Numeric], [@numeric], @symbol
|
37
|
-
assert_wrong_rtn [Numeric => Numeric], [@numeric], true
|
38
|
-
|
39
|
-
assert_wrong_rtn [Numeric, Numeric => Numeric], [@numeric, @numeric], @array
|
40
|
-
assert_wrong_rtn [Numeric, Numeric => Numeric], [@numeric, @numeric], @string
|
41
|
-
assert_wrong_rtn [Numeric, Numeric => Numeric], [@numeric, @numeric], @hash
|
42
|
-
assert_wrong_rtn [Numeric, Numeric => Numeric], [@numeric, @numeric], @symbol
|
43
|
-
assert_wrong_rtn [Numeric, Numeric => Numeric], [@numeric, @numeric], true
|
44
|
-
end
|
45
|
-
|
46
|
-
def test_wrong_args_type
|
47
|
-
assert_wrong_arg [Numeric => Numeric], [@array ], @numeric
|
48
|
-
assert_wrong_arg [Numeric => Numeric], [@string], @numeric
|
49
|
-
assert_wrong_arg [Numeric => Numeric], [@hash ], @numeric
|
50
|
-
assert_wrong_arg [Numeric => Numeric], [@symbol], @numeric
|
51
|
-
assert_wrong_arg [Numeric => Numeric], [true ], @numeric
|
52
|
-
|
53
|
-
assert_wrong_arg [Numeric, Numeric => Numeric], [@numeric, @array ], @numeric
|
54
|
-
assert_wrong_arg [Numeric, Numeric => Numeric], [@numeric, @string], @numeric
|
55
|
-
assert_wrong_arg [Numeric, Numeric => Numeric], [@numeric, @hash ], @numeric
|
56
|
-
assert_wrong_arg [Numeric, Numeric => Numeric], [@numeric, @symbol], @numeric
|
57
|
-
assert_wrong_arg [Numeric, Numeric => Numeric], [@numeric, true ], @numeric
|
58
|
-
end
|
59
|
-
|
60
|
-
def test_any
|
61
|
-
assert_correct_type [Any => Any], [@array ], @numeric
|
62
|
-
assert_correct_type [Any => Any], [@string], @numeric
|
63
|
-
assert_correct_type [Any => Any], [@hash ], @numeric
|
64
|
-
assert_correct_type [Any => Any], [@symbol], @numeric
|
65
|
-
|
66
|
-
assert_correct_type [Any, Any => Any], [@numeric, @array ], @numeric
|
67
|
-
assert_correct_type [Any, Any => Any], [@numeric, @string], @numeric
|
68
|
-
assert_correct_type [Any, Any => Any], [@numeric, @hash ], @numeric
|
69
|
-
assert_correct_type [Any, Any => Any], [@numeric, @symbol], @numeric
|
70
|
-
end
|
71
|
-
|
72
|
-
private
|
73
|
-
def assert_equal_to_s(str, val)
|
74
|
-
assert_equal str, val.to_s
|
75
|
-
end
|
76
|
-
|
77
|
-
def assert_correct_type(type_list, args, val)
|
78
|
-
assert_equal val, define_test_method(type_list, args, val).call(*args)
|
79
|
-
end
|
80
|
-
|
81
|
-
def assert_wrong_arg(type_list, args, val)
|
82
|
-
assert_raises(ArgumentError) { define_test_method(type_list, args, val).call(*args) }
|
83
|
-
end
|
84
|
-
|
85
|
-
def assert_wrong_rtn(type_list, args, val)
|
86
|
-
assert_raises(TypeError) { define_test_method(type_list, args, val).call(*args) }
|
87
|
-
end
|
88
|
-
|
89
|
-
def define_test_method(type_list, args, val)
|
90
|
-
klass = Class.new.class_eval <<-RUBY_CODE
|
91
|
-
def call(#{arg_literal(args.count)})
|
92
|
-
#{obj_literal(val)}
|
93
|
-
end
|
94
|
-
typesig call: #{obj_literal(type_list)}
|
95
|
-
RUBY_CODE
|
96
|
-
|
97
|
-
klass.new
|
98
|
-
end
|
99
|
-
|
100
|
-
def obj_literal(obj)
|
101
|
-
"ObjectSpace._id2ref(#{obj.__id__})"
|
102
|
-
end
|
103
|
-
|
104
|
-
def arg_literal(count)
|
105
|
-
('a'..'z').to_a[0..count-1].join(',')
|
106
|
-
end
|
107
|
-
end
|