#!/usr/bin/ruby
#
## Subsets
#
func failtest(f, msg) {
print "Testing: #{msg} -> "
var i = 0
try { f() }
catch { ++i }
assert_eq(i, 1)
print "ok\n"
}
#
## Inheritance from user-defined types
#
class Example(Number size) {
method display {
say "Number #{size}"
}
}
do {
subset Special < Example { .size == 42 }
func bar(x < Special) {
x.display
}
var wrong = Example(3)
var correct = Example(42)
bar(correct)
failtest(func{ bar(wrong) }, 'bar(wrong)')
}
#
## Union subset
#
do {
subset Union < Number,String;
func concat(x < Union, y < Union) {
x + y
}
say concat(1, 2)
say concat("a", "b")
failtest(func { concat([1], [2]) }, 'concat([],[])')
}
#
## Multiple subetting
#
subset Integer < Number { .is_int }
subset Natural < Integer { |n| n >= 0 }
subset EvenNatural < Natural { .is_even }
subset PowerOfTwo < EvenNatural { |n| (n & (n-1)) == 0 }
func natural(n < Natural) {
say "natural(): #{n}"
}
func even_natural(n < EvenNatural) {
say "even_natural(): #{n}"
}
func pow_2(n < PowerOfTwo) {
say "pow_2(): #{n}"
}
natural(5)
even_natural(42)
pow_2(64)
failtest(func { natural(-10) }, 'natural(-10)')
failtest(func { natural(3.5) }, 'natural(3.5)')
failtest(func { even_natural(21) }, 'even_natural(21)')
failtest(func { even_natural(42.5) }, 'even_natural(42.5)')
failtest(func { pow_2(5) }, 'pow_2(5)')
failtest(func { pow_2(42) }, 'pow_2(42)')
failtest(func { pow_2(128.1) }, 'pow_2(128.1)')
pow_2(128)
pow_2(2048)
even_natural(0)
even_natural(1_000_000)
natural(42)
natural(12345)
#
## Re-usage of subsets
#
func log_pow2(n < PowerOfTwo) {
say "log_pow2(): #{n.log(2)}"
}
log_pow2(1024)
log_pow2(4096)
failtest(func { log_pow2(42) }, 'log_pow2(42)')
failtest(func { log_pow2(63) }, 'log_pow2(63)')
#
## Recomposition
#
subset FortyTwo < EvenNatural { |n| n == 42 }
func forty_two(n < FortyTwo) {
say "forty_two(): #{n}"
}
forty_two(42)
failtest(func { forty_two(43) }, 'forty_two(43)')
failtest(func { forty_two(44) }, 'forty_two(44)')
failtest(func { forty_two("foo") }, 'forty_two("foo")')
#
## Subseting built-in types
#
func numeric(x < Number) {
say "numeric(): #{x}"
}
numeric(-42)
numeric(1234)
failtest(func { numeric("foo") }, 'numeric("foo")')
failtest(func { numeric({}) }, 'numeric({})')
func only_string (s < String) {
say s.dump
}
only_string("foo")
failtest(func { only_string(42) }, "only_string(42)")
failtest(func { only_string(File("foo")) }, "only_string(File('foo'))")
func filelish(f < File) {
say "filelish(): #{f.dump}"
}
filelish("foo") # File includes String
filelish(File("abc"))
failtest(func { filelish(42) }, "filelish(42)")
#
## This accepts any types that include the String class
#
func stringy(String s) {
say "stringy(): #{s.dump}"
}
stringy("foo")
stringy(File("abc"))
failtest(func { stringy(42) }, "stringy(42)")
#
## Multiple class subsetting
#
class Hello(name) {
method greet {
say "Hello, #{self.name}!"
}
}
class Hi < Hello {
method greet {
say "Hi, #{self.name}!"
}
}
class Hey < Hi {
method greet {
say "Hey, #{self.name}"
}
}
do {
func subset_greet(obj < Hi) { # `Hi` is the upper limit
obj.greet
}
var hi_obj = Hi("Foo")
subset_greet(hi_obj)
var hello_obj = Hello("Bar")
subset_greet(hello_obj)
var hey_obj = Hey("Baz")
failtest(func { subset_greet(42) }, "subset_greet(42)")
failtest(func { subset_greet(hey_obj) }, "subset_greet(hey_obj)")
#
## Limited greeting
#
func type_greet(Hi obj) { # HiGreeting is the lower limit
obj.greet
}
type_greet(hi_obj)
type_greet(hey_obj)
failtest(func { type_greet(42) }, "type_greet(42)")
failtest(func { type_greet(hello_obj) }, "type_greet(hello_obj)")
}
#
## Multiple dispatch greeting
#
do {
func multi_greet(obj < Hi) { obj.greet } # `Hi` is the upper limit
func multi_greet(Hi obj) { obj.greet } # `Hi` is the lower limit
multi_greet(Hi("Foo")) # ok
multi_greet(Hello("Bar")) # ok
multi_greet(Hey("Baz")) # fail: `Hey` is too evolved
multi_greet(Hi("Foo")) # ok
multi_greet(Hey("Baz")) # ok
multi_greet(Hello("Bar")) # fail: `Hello` is too primitive
}
#
## Subseting with `where` block
#
do {
func my_even(Number n < EvenNatural {|n| 10 < n }) {
say "my_even(): #{n}"
}
my_even(42)
failtest(func { my_even(8) }, "my_even(8)")
failtest(func { my_even(25) }, "my_even(25)")
failtest(func { my_even("foo") }, "my_even('foo')")
}
#
## PointLimit example
#
do {
subset PointLimit < Number {|n| n ~~ (-10 .. 10) }
class Point(x < PointLimit, y < PointLimit) {
method to_s { "[#{x},#{y}]" }
}
var p1 = Point(x: 5, y: -6)
var p2 = Point(y: 5, x: -6)
var p3 = Point(10, -10)
say p1
say p2
say p3
assert_eq("#{p1}", "[5,-6]")
assert_eq("#{p2}", "[-6,5]")
assert_eq("#{p3}", "[10,-10]")
failtest(func { Point(x: 5, y: 20) }, "Point(x:5, y:20)")
failtest(func { Point(x: -20, y: 5) }, "Point(x:-20, y:5)")
failtest(func { Point(-5, 20) }, "Point(-5, 20)")
failtest(func { Point(30, -4) }, "Point(30, -4)")
}
#
## Redeclarations
#
subset Scalar < Number,String;
subset Integer < Scalar { |n| n.is_int }
subset Natural < Integer { |n| n.is_pos }
subset EvenNatural < Natural { |n| n.is_even }
do {
func f(n < EvenNatural) {
say n
}
f(42)
f(2)
failtest(func { f(43) }, "f(43)")
failtest(func { f(-42) }, "f(-42)")
failtest(func { f("foo") }, "f('foo')")
}
#
## Subsets inside modules
#
module Greeting {
class Hello(name) {
method greet { say "Hello, #{self.name}!" }
}
class Hi < Hello {
method greet { say "Hi, #{self.name}!" }
}
subset Hello < Hi;
func greet(obj < Hi) { obj.greet } # `Hi` is the upper limit
greet(Hi("Foo")) # ok
greet(Hello("Bar")) # ok
}
#
## Subsets declared in modules and used outside
#
do {
module Hello {
subset NumStr < Num,Str
subset Numeric < Num
}
class Foo { }
class Subset::Test < Foo {
method foo (arg < Hello::Numeric) {
arg + 1
}
method bar (arg < Hello::NumStr) {
arg + 2
}
}
var obj = Subset::Test()
assert_eq(obj.foo(41), 42)
assert_eq(obj.bar(41), 43)
assert_eq(obj.bar("foo"), "foo2")
assert(Subset::Test.kind_of(Foo))
module Subset {
assert(Test.kind_of(main::Foo))
}
}
#
## Subsets without type
#
do {
subset Int { .is_int }
subset ShortLen { .len < 5 }
func foo(a < Int) { say a }
func bar(s < ShortLen) { say s }
foo(42)
bar("foo")
bar(1234)
bar([1,2,3])
failtest({ foo(sqrt(2)) }, "foo(sqrt(2))")
failtest({ foo("string") }, "foo(string)")
failtest({ bar("string") }, "bar(string)")
failtest({ bar([1,2,3,4,5]) }, "bar([1,2,3,4,5])")
}
#
## Subsets without type, with inheritance
#
do {
subset Int { .is_int }
subset Even < Int { .is_even }
func foo(n < Even) {
"#{n} -- ok"
}
assert_eq(foo(42), "42 -- ok")
failtest({ foo(42.5) }, "foo(42.5)")
failtest({ foo(43) }, "foo(43)")
failtest({ foo("string") }, "foo('string')")
assert_eq(foo(12), "12 -- ok")
}
do {
subset Integer < Number {|x| x.is_int }
subset Positive < Number {|x| x.is_pos }
subset Prime < Positive, Integer {|x| x.is_prime }
func foo(n < Prime) {
"#{n} -- ok"
}
assert_eq(foo(43), "43 -- ok")
failtest({ foo(43.5) }, "foo(43.5)")
failtest({ foo(42) }, "foo(42)")
failtest({ foo(-43) }, "foo(-43)")
failtest({ foo(-43.5) }, "foo(-43.5)")
failtest({ foo("string") }, "foo('string')")
assert_eq(foo(97), "97 -- ok")
}
do {
subset NonEmptyString < String { .len > 0 };
func bar(s < NonEmptyString) {
["bar", s]
}
func foo(NonEmptyString s) {
["foo", s]
}
assert_eq(bar("hi"), ["bar", "hi"])
assert_eq(foo("hi"), ["foo", "hi"])
assert_eq((try { bar("") } \\ 'bar("") error'), 'bar("") error')
assert_eq((try { foo("") } \\ 'foo("") error'), 'foo("") error')
}