From 6688cf1c9779dce87529392f3bbdcdcabcd81baa Mon Sep 17 00:00:00 2001 From: Joe Groff <arcata@gmail.com> Date: Sun, 26 Apr 2009 08:42:31 -0500 Subject: [PATCH 01/14] mopping up some noobsauce --- extra/roles/roles.factor | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extra/roles/roles.factor b/extra/roles/roles.factor index f9ce808eb8..d54b4339a7 100644 --- a/extra/roles/roles.factor +++ b/extra/roles/roles.factor @@ -8,8 +8,8 @@ IN: roles ERROR: role-slot-overlap class slots ; ERROR: multiple-inheritance-attempted classes ; -PREDICATE: role < class - { [ mixin-class? ] [ "role-slots" word-prop >boolean ] } 1&& ; +PREDICATE: role < mixin-class + "role-slots" word-prop >boolean ; : parse-role-definition ( -- class superroles slots ) CREATE-CLASS scan { From d2e293eb5ea779d2bfbbde84b76009748ab8de6b Mon Sep 17 00:00:00 2001 From: Joe Groff <arcata@gmail.com> Date: Sun, 26 Apr 2009 09:39:38 -0500 Subject: [PATCH 02/14] product virtual sequence --- extra/sequences/product/product-tests.factor | 24 ++++++++++------- extra/sequences/product/product.factor | 28 ++++++++++++++++++++ 2 files changed, 42 insertions(+), 10 deletions(-) create mode 100644 extra/sequences/product/product.factor diff --git a/extra/sequences/product/product-tests.factor b/extra/sequences/product/product-tests.factor index dfabc166ac..0a984072e0 100644 --- a/extra/sequences/product/product-tests.factor +++ b/extra/sequences/product/product-tests.factor @@ -1,19 +1,23 @@ -USING: arrays kernel sequences sequences.cartesian-product tools.test ; +USING: arrays kernel make sequences sequences.product tools.test ; IN: sequences.product.tests -[ - { { 0 "a" } { 1 "a" } { 2 "a" } { 0 "b" } { 1 "b" } { 2 "b" } } -] [ { { 0 1 2 } { "a" "b" } } [ ] cartesian-product-map ] unit-test + +[ { { 0 "a" } { 1 "a" } { 2 "a" } { 0 "b" } { 1 "b" } { 2 "b" } } ] +[ { { 0 1 2 } { "a" "b" } } <product-sequence> >array ] unit-test + +[ { { 0 "a" } { 1 "a" } { 2 "a" } { 0 "b" } { 1 "b" } { 2 "b" } } ] +[ { { 0 1 2 } { "a" "b" } } [ ] product-map ] unit-test [ { { 0 "a" t } { 1 "a" t } { 2 "a" t } { 0 "b" t } { 1 "b" t } { 2 "b" t } { 0 "a" f } { 1 "a" f } { 2 "a" f } { 0 "b" f } { 1 "b" f } { 2 "b" f } } -] [ { { 0 1 2 } { "a" "b" } { t f } } [ ] cartesian-product-map ] unit-test - -[ - { "012012" "aaabbb" } -] [ { { "0" "1" "2" } { "a" "b" } } [ [ first2 ] bi* [ append ] bi@ 2array ] cartesian-product-each ] unit-test - +] [ { { 0 1 2 } { "a" "b" } { t f } } [ ] product-map ] unit-test +[ "a1b1c1a2b2c2" ] [ + [ + { { "a" "b" "c" } { "1" "2" } } + [ [ % ] each ] product-each + ] "" make +] unit-test diff --git a/extra/sequences/product/product.factor b/extra/sequences/product/product.factor new file mode 100644 index 0000000000..73ba1e4e01 --- /dev/null +++ b/extra/sequences/product/product.factor @@ -0,0 +1,28 @@ +USING: accessors arrays kernel math sequences ; +IN: sequences.product + +TUPLE: product-sequence { sequences array read-only } { lengths array read-only } ; + +: <product-sequence> ( sequences -- product-sequence ) + >array dup [ length ] map product-sequence boa ; + +INSTANCE: product-sequence sequence + +M: product-sequence length lengths>> product ; + +: ns ( n lengths -- ns ) + [ V{ } clone ] 2dip [ /mod swap [ over push ] dip ] each drop ; + +: product@ ( n product-sequence -- ns seqs ) + [ lengths>> ns ] [ nip sequences>> ] 2bi ; + +M: product-sequence nth + product@ [ nth ] { } 2map-as ; + +M: product-sequence set-nth + immutable ; + +: product-map ( sequences quot -- sequence ) + [ <product-sequence> ] dip map ; inline +: product-each ( sequences quot -- ) + [ <product-sequence> ] dip each ; inline From e0f6825757892b7226853af7d54d38c33795bb71 Mon Sep 17 00:00:00 2001 From: Slava Pestov <slava@factorcode.org> Date: Sun, 26 Apr 2009 10:02:52 -0500 Subject: [PATCH 03/14] Rename some fields to avoid conflicting with windows.h macros 'small' and 'large' --- vm/code_gc.c | 16 ++++++++-------- vm/code_gc.h | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) mode change 100644 => 100755 vm/code_gc.h diff --git a/vm/code_gc.c b/vm/code_gc.c index e7fcfd3289..1405daa93f 100755 --- a/vm/code_gc.c +++ b/vm/code_gc.c @@ -22,13 +22,13 @@ void add_to_free_list(F_HEAP *heap, F_FREE_BLOCK *block) if(block->block.size < FREE_LIST_COUNT * BLOCK_SIZE_INCREMENT) { int index = block->block.size / BLOCK_SIZE_INCREMENT; - block->next_free = heap->free.small[index]; - heap->free.small[index] = block; + block->next_free = heap->free.small_blocks[index]; + heap->free.small_blocks[index] = block; } else { - block->next_free = heap->free.large; - heap->free.large = block; + block->next_free = heap->free.large_blocks; + heap->free.large_blocks = block; } } @@ -101,11 +101,11 @@ F_FREE_BLOCK *find_free_block(F_HEAP *heap, CELL size) while(attempt < FREE_LIST_COUNT * BLOCK_SIZE_INCREMENT) { int index = attempt / BLOCK_SIZE_INCREMENT; - F_FREE_BLOCK *block = heap->free.small[index]; + F_FREE_BLOCK *block = heap->free.small_blocks[index]; if(block) { assert_free_block(block); - heap->free.small[index] = block->next_free; + heap->free.small_blocks[index] = block->next_free; return block; } @@ -113,7 +113,7 @@ F_FREE_BLOCK *find_free_block(F_HEAP *heap, CELL size) } F_FREE_BLOCK *prev = NULL; - F_FREE_BLOCK *block = heap->free.large; + F_FREE_BLOCK *block = heap->free.large_blocks; while(block) { @@ -123,7 +123,7 @@ F_FREE_BLOCK *find_free_block(F_HEAP *heap, CELL size) if(prev) prev->next_free = block->next_free; else - heap->free.large = block->next_free; + heap->free.large_blocks = block->next_free; return block; } diff --git a/vm/code_gc.h b/vm/code_gc.h old mode 100644 new mode 100755 index 9b1e768a7b..d71dee29c5 --- a/vm/code_gc.h +++ b/vm/code_gc.h @@ -2,8 +2,8 @@ #define BLOCK_SIZE_INCREMENT 32 typedef struct { - F_FREE_BLOCK *small[FREE_LIST_COUNT]; - F_FREE_BLOCK *large; + F_FREE_BLOCK *small_blocks[FREE_LIST_COUNT]; + F_FREE_BLOCK *large_blocks; } F_HEAP_FREE_LIST; typedef struct { From 303ce55dc6d9ed566d05f36a995ba21200a56626 Mon Sep 17 00:00:00 2001 From: Joe Groff <arcata@gmail.com> Date: Sun, 26 Apr 2009 12:27:50 -0500 Subject: [PATCH 04/14] more efficient product-each and product-map that don't /mod all over the place --- extra/sequences/product/product-tests.factor | 6 ++- extra/sequences/product/product.factor | 50 ++++++++++++++++---- 2 files changed, 46 insertions(+), 10 deletions(-) diff --git a/extra/sequences/product/product-tests.factor b/extra/sequences/product/product-tests.factor index 0a984072e0..087d7a6175 100644 --- a/extra/sequences/product/product-tests.factor +++ b/extra/sequences/product/product-tests.factor @@ -5,8 +5,10 @@ IN: sequences.product.tests [ { { 0 "a" } { 1 "a" } { 2 "a" } { 0 "b" } { 1 "b" } { 2 "b" } } ] [ { { 0 1 2 } { "a" "b" } } <product-sequence> >array ] unit-test -[ { { 0 "a" } { 1 "a" } { 2 "a" } { 0 "b" } { 1 "b" } { 2 "b" } } ] -[ { { 0 1 2 } { "a" "b" } } [ ] product-map ] unit-test +: x ( n s -- sss ) <repetition> concat ; + +[ { "a" "aa" "aaa" "b" "bb" "bbb" } ] +[ { { 1 2 3 } { "a" "b" } } [ first2 x ] product-map ] unit-test [ { diff --git a/extra/sequences/product/product.factor b/extra/sequences/product/product.factor index 73ba1e4e01..0c5bb88f32 100644 --- a/extra/sequences/product/product.factor +++ b/extra/sequences/product/product.factor @@ -1,4 +1,4 @@ -USING: accessors arrays kernel math sequences ; +USING: accessors arrays kernel locals math sequences ; IN: sequences.product TUPLE: product-sequence { sequences array read-only } { lengths array read-only } ; @@ -10,19 +10,53 @@ INSTANCE: product-sequence sequence M: product-sequence length lengths>> product ; +<PRIVATE + : ns ( n lengths -- ns ) [ V{ } clone ] 2dip [ /mod swap [ over push ] dip ] each drop ; +: nths ( ns seqs -- nths ) + [ nth ] { } 2map-as ; + : product@ ( n product-sequence -- ns seqs ) [ lengths>> ns ] [ nip sequences>> ] 2bi ; +:: (carry-n) ( ns lengths i -- ) + ns length i 1+ = [ + i ns nth i lengths nth = [ + 0 i ns set-nth + i 1+ ns [ 1+ ] change-nth + ns lengths i 1+ (carry-n) + ] when + ] unless ; + +: carry-ns ( ns lengths -- ) + 0 (carry-n) ; + +: product-iter ( ns lengths -- ) + [ 0 over [ 1+ ] change-nth ] dip carry-ns ; + +: start-product-iter ( sequence-product -- ns lengths ) + [ [ drop 0 ] map ] [ [ length ] map ] bi ; + +: end-product-iter? ( ns lengths -- ? ) + [ 1 tail* first ] bi@ = ; + +PRIVATE> + M: product-sequence nth - product@ [ nth ] { } 2map-as ; + product@ nths ; -M: product-sequence set-nth - immutable ; +:: product-each ( sequences quot -- ) + sequences start-product-iter :> lengths :> ns + [ ns lengths end-product-iter? ] + [ ns sequences nths quot call ns lengths product-iter ] until ; inline + +:: product-map ( sequences quot -- sequence ) + 0 :> i! + sequences [ length ] [ * ] map-reduce sequences + [| result | + sequences [ quot call i result set-nth i 1+ i! ] product-each + result + ] new-like ; inline -: product-map ( sequences quot -- sequence ) - [ <product-sequence> ] dip map ; inline -: product-each ( sequences quot -- ) - [ <product-sequence> ] dip each ; inline From f007c281e3b2c0645a095b1f9febb31668eea9e9 Mon Sep 17 00:00:00 2001 From: Joe Groff <arcata@gmail.com> Date: Sun, 26 Apr 2009 13:08:15 -0500 Subject: [PATCH 05/14] docs for sequences.product --- extra/sequences/product/product-docs.factor | 60 +++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 extra/sequences/product/product-docs.factor diff --git a/extra/sequences/product/product-docs.factor b/extra/sequences/product/product-docs.factor new file mode 100644 index 0000000000..6033767f47 --- /dev/null +++ b/extra/sequences/product/product-docs.factor @@ -0,0 +1,60 @@ +USING: help.markup help.syntax multiline quotations sequences sequences.product ; +IN: sequences + +HELP: product-sequence +{ $class-description "A class of virtual sequences that present the cartesian product of their underlying set of sequences. Product sequences are constructed with the " { $link <product-sequence> } " word." } +{ $examples +{ $example <" USING: arrays prettyprint sequences.product ; +{ { 1 2 3 } { "a" "b" "c" } } <product-sequence> >array . +"> <" { + { 1 "a" } + { 2 "a" } + { 3 "a" } + { 1 "b" } + { 2 "b" } + { 3 "b" } + { 1 "c" } + { 2 "c" } + { 3 "c" } +}"> } } ; + +HELP: <product-sequence> +{ $values { "sequences" sequence } { "product-sequence" product-sequence } } +{ $description "Constructs a " { $link product-sequence } " over " { $snippet "sequences" } "." } +{ $examples +{ $example <" USING: arrays prettyprint sequences.product ; +{ { 1 2 3 } { "a" "b" "c" } } <product-sequence> >array . +"> <" { + { 1 "a" } + { 2 "a" } + { 3 "a" } + { 1 "b" } + { 2 "b" } + { 3 "b" } + { 1 "c" } + { 2 "c" } + { 3 "c" } +}"> } } ; + +{ product-sequence <product-sequence> } related-words + +HELP: product-map +{ $values { "sequences" sequence } { "quot" { $quotation "( sequence -- value )" } } { "sequence" sequence } } +{ $description "Calls " { $snippet "quot" } " for every element of the cartesian product of " { $snippet "sequences" } " and collects the results from " { $snippet "quot" } " into an output sequence." } +{ $notes { $snippet "[ ... ] product-map" } " is equivalent to, but more efficient than, " { $snippet "<product-sequence> [ ... ] map" } "." } ; + +HELP: product-each +{ $values { "sequences" sequence } { "quot" { $quotation "( sequence -- )" } } } +{ $description "Calls " { $snippet "quot" } " for every element of the cartesian product of " { $snippet "sequences" } "." } +{ $notes { $snippet "[ ... ] product-each" } " is equivalent to, but more efficient than, " { $snippet "<product-sequence> [ ... ] each" } "." } ; + +{ product-map product-each } related-words + +ARTICLE: "sequences.product" "Product sequences" +"The " { $vocab-link "sequences.product" } " vocabulary provides a virtual sequence and combinators for manipulating the cartesian product of a set of sequences." +{ $subsection product-sequence } +{ $subsection <product-sequence> } +{ $subsection product-map } +{ $subsection product-each } ; + +ABOUT: "sequences.product" From a2056d932c3c02fd6ecc5673a3f75ee067648990 Mon Sep 17 00:00:00 2001 From: Joe Groff <arcata@gmail.com> Date: Sun, 26 Apr 2009 13:09:30 -0500 Subject: [PATCH 06/14] gold plating for sequences.product --- extra/sequences/product/authors.txt | 1 + extra/sequences/product/product-docs.factor | 1 + extra/sequences/product/product-tests.factor | 1 + extra/sequences/product/product.factor | 1 + extra/sequences/product/summary.txt | 1 + 5 files changed, 5 insertions(+) create mode 100644 extra/sequences/product/authors.txt create mode 100644 extra/sequences/product/summary.txt diff --git a/extra/sequences/product/authors.txt b/extra/sequences/product/authors.txt new file mode 100644 index 0000000000..f13c9c1e77 --- /dev/null +++ b/extra/sequences/product/authors.txt @@ -0,0 +1 @@ +Joe Groff diff --git a/extra/sequences/product/product-docs.factor b/extra/sequences/product/product-docs.factor index 6033767f47..b7dcaa626e 100644 --- a/extra/sequences/product/product-docs.factor +++ b/extra/sequences/product/product-docs.factor @@ -1,3 +1,4 @@ +! (c)2009 Joe Groff bsd license USING: help.markup help.syntax multiline quotations sequences sequences.product ; IN: sequences diff --git a/extra/sequences/product/product-tests.factor b/extra/sequences/product/product-tests.factor index 087d7a6175..5e0997dc2e 100644 --- a/extra/sequences/product/product-tests.factor +++ b/extra/sequences/product/product-tests.factor @@ -1,3 +1,4 @@ +! (c)2009 Joe Groff bsd license USING: arrays kernel make sequences sequences.product tools.test ; IN: sequences.product.tests diff --git a/extra/sequences/product/product.factor b/extra/sequences/product/product.factor index 0c5bb88f32..665d43f0cd 100644 --- a/extra/sequences/product/product.factor +++ b/extra/sequences/product/product.factor @@ -1,3 +1,4 @@ +! (c)2009 Joe Groff bsd license USING: accessors arrays kernel locals math sequences ; IN: sequences.product diff --git a/extra/sequences/product/summary.txt b/extra/sequences/product/summary.txt new file mode 100644 index 0000000000..c234c84a94 --- /dev/null +++ b/extra/sequences/product/summary.txt @@ -0,0 +1 @@ +Cartesian products of sequences From 291ac48a1766e942a20099d023ba3e84deee5609 Mon Sep 17 00:00:00 2001 From: Slava Pestov <slava@slava-pestovs-macbook-pro.local> Date: Sun, 26 Apr 2009 13:31:10 -0500 Subject: [PATCH 07/14] tuple-arrays: completely rewritten to use functors, 10x faster on benchmark --- basis/inverse/inverse.factor | 2 +- basis/tuple-arrays/authors.txt | 2 +- basis/tuple-arrays/summary.txt | 1 - basis/tuple-arrays/tags.txt | 1 - basis/tuple-arrays/tuple-arrays-docs.factor | 13 ---- basis/tuple-arrays/tuple-arrays-tests.factor | 16 ++-- basis/tuple-arrays/tuple-arrays.factor | 76 ++++++++++++++----- extra/benchmark/tuple-arrays/authors.txt | 1 + .../tuple-arrays/tuple-arrays.factor | 20 +++++ 9 files changed, 88 insertions(+), 44 deletions(-) delete mode 100644 basis/tuple-arrays/summary.txt delete mode 100644 basis/tuple-arrays/tags.txt delete mode 100644 basis/tuple-arrays/tuple-arrays-docs.factor create mode 100644 extra/benchmark/tuple-arrays/authors.txt create mode 100644 extra/benchmark/tuple-arrays/tuple-arrays.factor diff --git a/basis/inverse/inverse.factor b/basis/inverse/inverse.factor index a988063293..0b86b02e92 100755 --- a/basis/inverse/inverse.factor +++ b/basis/inverse/inverse.factor @@ -12,7 +12,7 @@ IN: inverse ERROR: fail ; M: fail summary drop "Matching failed" ; -: assure ( ? -- ) [ fail ] unless ; +: assure ( ? -- ) [ fail ] unless ; inline : =/fail ( obj1 obj2 -- ) = assure ; diff --git a/basis/tuple-arrays/authors.txt b/basis/tuple-arrays/authors.txt index f990dd0ed2..d4f5d6b3ae 100644 --- a/basis/tuple-arrays/authors.txt +++ b/basis/tuple-arrays/authors.txt @@ -1 +1 @@ -Daniel Ehrenberg +Slava Pestov \ No newline at end of file diff --git a/basis/tuple-arrays/summary.txt b/basis/tuple-arrays/summary.txt deleted file mode 100644 index ac05ae9bcc..0000000000 --- a/basis/tuple-arrays/summary.txt +++ /dev/null @@ -1 +0,0 @@ -Packed homogeneous tuple arrays diff --git a/basis/tuple-arrays/tags.txt b/basis/tuple-arrays/tags.txt deleted file mode 100644 index 42d711b32b..0000000000 --- a/basis/tuple-arrays/tags.txt +++ /dev/null @@ -1 +0,0 @@ -collections diff --git a/basis/tuple-arrays/tuple-arrays-docs.factor b/basis/tuple-arrays/tuple-arrays-docs.factor deleted file mode 100644 index 18f5547e7f..0000000000 --- a/basis/tuple-arrays/tuple-arrays-docs.factor +++ /dev/null @@ -1,13 +0,0 @@ -USING: help.syntax help.markup splitting kernel sequences ; -IN: tuple-arrays - -HELP: tuple-array -{ $description "The class of packed homogeneous tuple arrays. They are created with " { $link <tuple-array> } ". All elements are of the same tuple class. Mutations done to an element are not copied back to the packed array unless it is explicitly written back. To convert a sequence to a tuple array, use the word " { $link >tuple-array } "." } ; - -HELP: <tuple-array> -{ $values { "class" "a tuple class" } { "length" "a non-negative integer" } { "tuple-array" tuple-array } } -{ $description "Creates an instance of the " { $link <tuple-array> } " class with the given length and containing the given tuple class." } ; - -HELP: >tuple-array -{ $values { "seq" sequence } { "tuple-array" tuple-array } } -{ $description "Converts a sequence into a homogeneous unboxed tuple array of the type indicated by the first element." } ; diff --git a/basis/tuple-arrays/tuple-arrays-tests.factor b/basis/tuple-arrays/tuple-arrays-tests.factor index 7aa49b880f..4606ecdada 100644 --- a/basis/tuple-arrays/tuple-arrays-tests.factor +++ b/basis/tuple-arrays/tuple-arrays-tests.factor @@ -5,17 +5,21 @@ IN: tuple-arrays.tests SYMBOL: mat TUPLE: foo bar ; C: <foo> foo -[ 2 ] [ 2 foo <tuple-array> dup mat set length ] unit-test +TUPLE-ARRAY: foo + +[ 2 ] [ 2 <foo-array> dup mat set length ] unit-test [ T{ foo } ] [ mat get first ] unit-test [ T{ foo f 2 } ] [ T{ foo f 2 } 0 mat get [ set-nth ] keep first ] unit-test -[ t ] [ { T{ foo f 1 } T{ foo f 2 } } >tuple-array dup mat set tuple-array? ] unit-test +[ t ] [ { T{ foo f 1 } T{ foo f 2 } } >foo-array dup mat set foo-array? ] unit-test [ T{ foo f 3 } t ] -[ mat get [ bar>> 2 + <foo> ] map [ first ] keep tuple-array? ] unit-test +[ mat get [ bar>> 2 + <foo> ] map [ first ] keep foo-array? ] unit-test -[ 2 ] [ 2 foo <tuple-array> dup mat set length ] unit-test +[ 2 ] [ 2 <foo-array> dup mat set length ] unit-test [ T{ foo } ] [ mat get first ] unit-test [ T{ foo f 1 } ] [ T{ foo f 1 } 0 mat get [ set-nth ] keep first ] unit-test TUPLE: baz { bing integer } bong ; -[ 0 ] [ 1 baz <tuple-array> first bing>> ] unit-test -[ f ] [ 1 baz <tuple-array> first bong>> ] unit-test +TUPLE-ARRAY: baz + +[ 0 ] [ 1 <baz-array> first bing>> ] unit-test +[ f ] [ 1 <baz-array> first bong>> ] unit-test diff --git a/basis/tuple-arrays/tuple-arrays.factor b/basis/tuple-arrays/tuple-arrays.factor index af62c0b0d7..466262f3e0 100644 --- a/basis/tuple-arrays/tuple-arrays.factor +++ b/basis/tuple-arrays/tuple-arrays.factor @@ -1,34 +1,68 @@ -! Copyright (C) 2007 Daniel Ehrenberg. +! Copyright (C) 2009 Slava Pestov. ! See http://factorcode.org/license.txt for BSD license. -USING: splitting grouping classes.tuple classes math kernel -sequences arrays accessors ; +USING: accessors arrays combinators.smart fry functors grouping +kernel macros sequences sequences.private stack-checker +parser ; +FROM: inverse => undo ; IN: tuple-arrays -TUPLE: tuple-array { seq read-only } { class read-only } ; +<PRIVATE -: <tuple-array> ( length class -- tuple-array ) - [ - new tuple>array 1 tail - [ <repetition> concat ] [ length ] bi <sliced-groups> - ] [ ] bi tuple-array boa ; +MACRO: infer-in ( class -- quot ) infer in>> '[ _ ] ; -M: tuple-array nth - [ seq>> nth ] [ class>> ] bi prefix >tuple ; +: smart-tuple>array ( tuple class -- array ) + '[ [ _ boa ] undo ] output>array ; inline -M: tuple-array set-nth ( elt n seq -- ) - [ tuple>array 1 tail ] 2dip seq>> set-nth ; +: smart-array>tuple ( array class -- tuple ) + '[ _ boa ] input<sequence ; inline -M: tuple-array new-sequence - class>> <tuple-array> ; +: tuple-arity ( class -- quot ) '[ _ boa ] infer-in ; inline -: >tuple-array ( seq -- tuple-array ) +: tuple-prototype ( class -- array ) + [ new ] [ smart-tuple>array ] bi ; inline + +PRIVATE> + +FUNCTOR: define-tuple-array ( CLASS -- ) + +CLASS IS ${CLASS} + +CLASS-array DEFINES-CLASS ${CLASS}-array +CLASS-array? IS ${CLASS-array}? + +<CLASS-array> DEFINES <${CLASS}-array> +>CLASS-array DEFINES >${CLASS}-array + +WHERE + +TUPLE: CLASS-array { seq sliced-groups read-only } ; + +: <CLASS-array> ( length -- tuple-array ) + CLASS tuple-prototype <repetition> concat + CLASS tuple-arity <sliced-groups> + CLASS-array boa ; + +M: CLASS-array nth-unsafe + seq>> nth-unsafe CLASS smart-array>tuple ; + +M: CLASS-array set-nth-unsafe + [ CLASS smart-tuple>array ] 2dip seq>> set-nth-unsafe ; + +M: CLASS-array new-sequence + drop <CLASS-array> ; + +: >CLASS-array ( seq -- tuple-array ) dup empty? [ - 0 over first class <tuple-array> clone-like + 0 <CLASS-array> clone-like ] unless ; -M: tuple-array like - drop dup tuple-array? [ >tuple-array ] unless ; +M: CLASS-array like + drop dup CLASS-array? [ >CLASS-array ] unless ; -M: tuple-array length seq>> length ; +M: CLASS-array length seq>> length ; -INSTANCE: tuple-array sequence +INSTANCE: CLASS-array sequence + +;FUNCTOR + +SYNTAX: TUPLE-ARRAY: scan-word define-tuple-array ; diff --git a/extra/benchmark/tuple-arrays/authors.txt b/extra/benchmark/tuple-arrays/authors.txt new file mode 100644 index 0000000000..d4f5d6b3ae --- /dev/null +++ b/extra/benchmark/tuple-arrays/authors.txt @@ -0,0 +1 @@ +Slava Pestov \ No newline at end of file diff --git a/extra/benchmark/tuple-arrays/tuple-arrays.factor b/extra/benchmark/tuple-arrays/tuple-arrays.factor new file mode 100644 index 0000000000..483311d4f4 --- /dev/null +++ b/extra/benchmark/tuple-arrays/tuple-arrays.factor @@ -0,0 +1,20 @@ +! Copyright (C) 2009 Slava Pestov. +! See http://factorcode.org/license.txt for BSD license. +USING: kernel math math.functions tuple-arrays accessors fry sequences +prettyprint ; +IN: benchmark.tuple-arrays + +TUPLE: point { x float } { y float } { z float } ; + +TUPLE-ARRAY: point + +: tuple-array-benchmark ( -- ) + 100 [ + drop 5000 <point-array> [ + [ 1+ ] change-x + [ 1- ] change-y + [ 1+ 2 / ] change-z + ] map [ z>> ] sigma + ] sigma . ; + +MAIN: tuple-array-benchmark \ No newline at end of file From 06012cf2917e632f3b14c1a80c221b59e1f383b4 Mon Sep 17 00:00:00 2001 From: Joe Groff <arcata@gmail.com> Date: Sun, 26 Apr 2009 14:58:31 -0500 Subject: [PATCH 08/14] order-insensitive pair methods --- extra/pair-methods/authors.txt | 1 + extra/pair-methods/pair-methods-tests.factor | 43 +++++++++++++++ extra/pair-methods/pair-methods.factor | 57 ++++++++++++++++++++ extra/pair-methods/summary.txt | 1 + 4 files changed, 102 insertions(+) create mode 100644 extra/pair-methods/authors.txt create mode 100644 extra/pair-methods/pair-methods-tests.factor create mode 100644 extra/pair-methods/pair-methods.factor create mode 100644 extra/pair-methods/summary.txt diff --git a/extra/pair-methods/authors.txt b/extra/pair-methods/authors.txt new file mode 100644 index 0000000000..f13c9c1e77 --- /dev/null +++ b/extra/pair-methods/authors.txt @@ -0,0 +1 @@ +Joe Groff diff --git a/extra/pair-methods/pair-methods-tests.factor b/extra/pair-methods/pair-methods-tests.factor new file mode 100644 index 0000000000..f88ca966aa --- /dev/null +++ b/extra/pair-methods/pair-methods-tests.factor @@ -0,0 +1,43 @@ +! (c)2009 Joe Groff bsd license +USING: accessors pair-methods classes kernel sequences tools.test ; +IN: pair-methods.tests + +TUPLE: thang ; + +TUPLE: foom < thang ; +TUPLE: barm < foom ; + +TUPLE: zim < thang ; +TUPLE: zang < zim ; + +: class-names ( a b prefix -- string ) + [ [ class name>> ] bi@ "-" glue ] dip prepend ; + +PAIR-GENERIC: blibble ( a b -- c ) + +PAIR-M: thang thang blibble + "vanilla " class-names ; + +PAIR-M: foom thang blibble + "chocolate " class-names ; + +PAIR-M: barm thang blibble + "strawberry " class-names ; + +PAIR-M: barm zim blibble + "coconut " class-names ; + +[ "vanilla zang-zim" ] [ zim new zang new blibble ] unit-test + +! args automatically swap to match most specific method +[ "chocolate foom-zim" ] [ foom new zim new blibble ] unit-test +[ "chocolate foom-zim" ] [ zim new foom new blibble ] unit-test + +[ "strawberry barm-barm" ] [ barm new barm new blibble ] unit-test +[ "strawberry barm-foom" ] [ barm new foom new blibble ] unit-test +[ "strawberry barm-foom" ] [ foom new barm new blibble ] unit-test + +[ "coconut barm-zang" ] [ zang new barm new blibble ] unit-test +[ "coconut barm-zim" ] [ barm new zim new blibble ] unit-test + +[ 1 2 blibble ] [ no-pair-method? ] must-fail-with diff --git a/extra/pair-methods/pair-methods.factor b/extra/pair-methods/pair-methods.factor new file mode 100644 index 0000000000..d44d5bce78 --- /dev/null +++ b/extra/pair-methods/pair-methods.factor @@ -0,0 +1,57 @@ +! (c)2009 Joe Groff bsd license +USING: arrays assocs classes classes.tuple.private combinators +effects.parser generic.parser kernel math math.order parser +quotations sequences sorting words ; +IN: pair-methods + +ERROR: no-pair-method a b generic ; + +: ?swap ( a b ? -- a/b b/a ) + [ swap ] when ; + +: method-sort-key ( pair -- key ) + first2 [ tuple-layout third ] bi@ + ; + +: pair-match-condition ( pair -- quot ) + first2 [ [ instance? ] swap prefix ] bi@ [ ] 2sequence + [ 2dup ] [ bi* and ] surround ; + +: pair-method-cond ( pair quot -- array ) + [ pair-match-condition ] [ ] bi* 2array ; + +: sorted-pair-methods ( word -- alist ) + "pair-generic-methods" word-prop >alist + [ [ first method-sort-key ] bi@ >=< ] sort ; + +: pair-generic-definition ( word -- def ) + [ sorted-pair-methods [ first2 pair-method-cond ] map ] + [ [ no-pair-method ] curry suffix ] bi 1quotation + [ 2dup [ class ] bi@ <=> +gt+ eq? ?swap ] [ cond ] surround ; + +: make-pair-generic ( word -- ) + dup pair-generic-definition define ; + +: define-pair-generic ( word effect -- ) + [ swap set-stack-effect ] + [ drop H{ } clone "pair-generic-methods" set-word-prop ] + [ drop make-pair-generic ] 2tri ; + +: (PAIR-GENERIC:) ( -- ) + CREATE-GENERIC complete-effect define-pair-generic ; + +SYNTAX: PAIR-GENERIC: (PAIR-GENERIC:) ; + +: define-pair-method ( a b pair-generic definition -- ) + [ 2array ] 2dip swap + [ "pair-generic-methods" word-prop [ swap ] dip set-at ] + [ make-pair-generic ] bi ; + +: ?prefix-swap ( quot ? -- quot' ) + [ \ swap prefix ] when ; + +: (PAIR-M:) ( -- ) + scan-word scan-word 2dup <=> +gt+ eq? [ + ?swap scan-word parse-definition + ] keep ?prefix-swap define-pair-method ; + +SYNTAX: PAIR-M: (PAIR-M:) ; diff --git a/extra/pair-methods/summary.txt b/extra/pair-methods/summary.txt new file mode 100644 index 0000000000..823bc712f6 --- /dev/null +++ b/extra/pair-methods/summary.txt @@ -0,0 +1 @@ +Order-insensitive double dispatch generics From dac5203e81b6f40ac3660e1e4fdecec4a29a8678 Mon Sep 17 00:00:00 2001 From: Slava Pestov <slava@slava-pestovs-macbook-pro.local> Date: Sun, 26 Apr 2009 16:04:44 -0500 Subject: [PATCH 09/14] compiler.tree.builder: Fix scoping of a variable by hints vocab --- basis/compiler/tree/builder/builder.factor | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/basis/compiler/tree/builder/builder.factor b/basis/compiler/tree/builder/builder.factor index 7f760650e7..37cc1f05da 100644 --- a/basis/compiler/tree/builder/builder.factor +++ b/basis/compiler/tree/builder/builder.factor @@ -54,15 +54,16 @@ PRIVATE> #! This slows down compiler.tree.propagation.inlining since then every #! inlined usage of a method has an inline-dependency on the mixin, and #! not the more specific type at the call site. - specialize-method? off - [ - #call in-d>> word/quot build-tree-with unclip-last in-d>> :> in-d - { - { [ dup not ] [ ] } - { [ dup ends-with-terminate? ] [ #call out-d>> [ f swap #push ] map append ] } - [ in-d #call out-d>> #copy suffix ] - } cond - ] [ dup inference-error? [ drop f ] [ rethrow ] if ] recover ; + f specialize-method? [ + [ + #call in-d>> word/quot build-tree-with unclip-last in-d>> :> in-d + { + { [ dup not ] [ ] } + { [ dup ends-with-terminate? ] [ #call out-d>> [ f swap #push ] map append ] } + [ in-d #call out-d>> #copy suffix ] + } cond + ] [ dup inference-error? [ drop f ] [ rethrow ] if ] recover + ] with-variable ; : contains-breakpoints? ( word -- ? ) def>> [ word? ] filter [ "break?" word-prop ] any? ; From 087c962f75d432cdd533991f403364e4782f83d5 Mon Sep 17 00:00:00 2001 From: Slava Pestov <slava@slava-pestovs-macbook-pro.local> Date: Sun, 26 Apr 2009 16:05:09 -0500 Subject: [PATCH 10/14] VM: simplify GC a bit, add GC_DEBUG compile-time flag --- vm/code_block.c | 3 ++- vm/data_gc.c | 4 ++-- vm/data_gc.h | 20 ++++++++++++++++++-- vm/data_heap.h | 1 - 4 files changed, 22 insertions(+), 6 deletions(-) diff --git a/vm/code_block.c b/vm/code_block.c index 8dda8bc16e..1ce440c9ab 100644 --- a/vm/code_block.c +++ b/vm/code_block.c @@ -224,7 +224,8 @@ void mark_object_code_block(CELL scan) { case WORD_TYPE: word = (F_WORD *)scan; - mark_code_block(word->code); + if(word->code) + mark_code_block(word->code); if(word->profiling) mark_code_block(word->profiling); break; diff --git a/vm/data_gc.c b/vm/data_gc.c index 3ab2055d82..a1a86e7789 100755 --- a/vm/data_gc.c +++ b/vm/data_gc.c @@ -330,7 +330,7 @@ CELL copy_next_from_tenured(CELL scan) void copy_reachable_objects(CELL scan, CELL *end) { - if(HAVE_NURSERY_P && collecting_gen == NURSERY) + if(collecting_gen == NURSERY) { while(scan < *end) scan = copy_next_from_nursery(scan); @@ -405,7 +405,7 @@ void end_gc(CELL gc_elapsed) if(collecting_gen != NURSERY) reset_generations(NURSERY,collecting_gen - 1); } - else if(HAVE_NURSERY_P && collecting_gen == NURSERY) + else if(collecting_gen == NURSERY) { nursery.here = nursery.start; } diff --git a/vm/data_gc.h b/vm/data_gc.h index 52d8b603ad..afa45c5522 100755 --- a/vm/data_gc.h +++ b/vm/data_gc.h @@ -58,7 +58,7 @@ INLINE bool should_copy(CELL untagged) return true; else if(HAVE_AGING_P && collecting_gen == AGING) return !in_zone(&data_heap->generations[TENURED],untagged); - else if(HAVE_NURSERY_P && collecting_gen == NURSERY) + else if(collecting_gen == NURSERY) return in_zone(&nursery,untagged); else { @@ -78,15 +78,31 @@ allocation (which does not call GC because of possible roots in volatile registers) does not run out of memory */ #define ALLOT_BUFFER_ZONE 1024 +/* If this is defined, we GC every 100 allocations. This catches missing local roots */ +#ifdef GC_DEBUG +static int count; +#endif + /* * It is up to the caller to fill in the object's fields in a meaningful * fashion! */ INLINE void *allot_object(CELL type, CELL a) { + +#ifdef GC_DEBUG + + if(!gc_off) + { + if(count++ % 1000 == 0) + gc(); + + } +#endif + CELL *object; - if(HAVE_NURSERY_P && nursery.size - ALLOT_BUFFER_ZONE > a) + if(nursery.size - ALLOT_BUFFER_ZONE > a) { /* If there is insufficient room, collect the nursery */ if(nursery.here + ALLOT_BUFFER_ZONE + a > nursery.end) diff --git a/vm/data_heap.h b/vm/data_heap.h index a7f44e73f8..5836967295 100644 --- a/vm/data_heap.h +++ b/vm/data_heap.h @@ -37,7 +37,6 @@ F_DATA_HEAP *data_heap; /* the 0th generation is where new objects are allocated. */ #define NURSERY 0 -#define HAVE_NURSERY_P (data_heap->gen_count>1) /* where objects hang around */ #define AGING (data_heap->gen_count-2) #define HAVE_AGING_P (data_heap->gen_count>2) From 6b5b839e727212c05df51ceefcb2f60854bf9a7c Mon Sep 17 00:00:00 2001 From: Slava Pestov <slava@slava-pestovs-macbook-pro.local> Date: Sun, 26 Apr 2009 16:05:23 -0500 Subject: [PATCH 11/14] Makefile: add SITE_CFLAGS even if DEBUG=1 --- Makefile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index db99120a77..c19d83e58e 100644 --- a/Makefile +++ b/Makefile @@ -15,9 +15,11 @@ FFI_TEST_CFLAGS = -fPIC ifdef DEBUG CFLAGS += -g else - CFLAGS += -O3 $(SITE_CFLAGS) + CFLAGS += -O3 endif +CFLAGS += $(SITE_CFLAGS) + ENGINE = $(DLL_PREFIX)factor$(DLL_SUFFIX)$(DLL_EXTENSION) ifdef CONFIG From 7094b78821bf4817af674284d2a40f070b46e894 Mon Sep 17 00:00:00 2001 From: Slava Pestov <slava@slava-pestovs-macbook-pro.local> Date: Sun, 26 Apr 2009 21:22:06 -0500 Subject: [PATCH 12/14] Add firstn-unsafe, input<sequence-unsafe --- basis/combinators/smart/smart.factor | 4 ++++ basis/generalizations/generalizations.factor | 9 ++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/basis/combinators/smart/smart.factor b/basis/combinators/smart/smart.factor index aa7960539c..9519847810 100644 --- a/basis/combinators/smart/smart.factor +++ b/basis/combinators/smart/smart.factor @@ -18,6 +18,10 @@ MACRO: input<sequence ( quot -- newquot ) [ infer in>> ] keep '[ _ firstn @ ] ; +MACRO: input<sequence-unsafe ( quot -- newquot ) + [ infer in>> ] keep + '[ _ firstn-unsafe @ ] ; + MACRO: reduce-outputs ( quot operation -- newquot ) [ dup infer out>> 1 [-] ] dip n*quot compose ; diff --git a/basis/generalizations/generalizations.factor b/basis/generalizations/generalizations.factor index edee44acc6..139b7a528a 100644 --- a/basis/generalizations/generalizations.factor +++ b/basis/generalizations/generalizations.factor @@ -26,11 +26,14 @@ MACRO: narray ( n -- ) MACRO: nsum ( n -- ) 1- [ + ] n*quot ; +MACRO: firstn-unsafe ( n -- ) + [ '[ [ _ ] dip nth-unsafe ] ] map '[ _ cleave ] ; + MACRO: firstn ( n -- ) dup zero? [ drop [ drop ] ] [ - [ [ '[ [ _ ] dip nth-unsafe ] ] map ] - [ 1- '[ [ _ ] dip bounds-check 2drop ] ] - bi prefix '[ _ cleave ] + [ 1- swap bounds-check 2drop ] + [ firstn-unsafe ] + bi-curry '[ _ _ bi ] ] if ; MACRO: npick ( n -- ) From 58cba832a0ab29580bb29a2a86d3557a02be81a9 Mon Sep 17 00:00:00 2001 From: Slava Pestov <slava@slava-pestovs-macbook-pro.local> Date: Sun, 26 Apr 2009 21:22:20 -0500 Subject: [PATCH 13/14] functors: add support for call-next-method --- basis/functors/functors.factor | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/basis/functors/functors.factor b/basis/functors/functors.factor index 309154fb49..6afa020128 100644 --- a/basis/functors/functors.factor +++ b/basis/functors/functors.factor @@ -18,6 +18,8 @@ IN: functors : define-declared* ( word def effect -- ) pick set-word define-declared ; +TUPLE: fake-call-next-method ; + TUPLE: fake-quotation seq ; GENERIC: >fake-quotations ( quot -- fake ) @@ -29,17 +31,25 @@ M: array >fake-quotations [ >fake-quotations ] { } map-as ; M: object >fake-quotations ; -GENERIC: fake-quotations> ( fake -- quot ) +GENERIC: (fake-quotations>) ( fake -- ) -M: fake-quotation fake-quotations> - seq>> [ fake-quotations> ] [ ] map-as ; +: fake-quotations> ( fake -- quot ) + [ (fake-quotations>) ] [ ] make ; -M: array fake-quotations> [ fake-quotations> ] map ; +M: fake-quotation (fake-quotations>) + [ seq>> [ (fake-quotations>) ] each ] [ ] make , ; -M: object fake-quotations> ; +M: array (fake-quotations>) + [ [ (fake-quotations>) ] each ] { } make , ; + +M: fake-call-next-method (fake-quotations>) + drop method-body get literalize , \ (call-next-method) , ; + +M: object (fake-quotations>) , ; : parse-definition* ( accum -- accum ) - parse-definition >fake-quotations parsed \ fake-quotations> parsed ; + parse-definition >fake-quotations parsed + [ fake-quotations> first ] over push-all ; : parse-declared* ( accum -- accum ) complete-effect @@ -64,7 +74,7 @@ SYNTAX: `TUPLE: SYNTAX: `M: scan-param parsed scan-param parsed - \ create-method-in parsed + [ create-method-in dup method-body set ] over push-all parse-definition* \ define* parsed ; @@ -92,6 +102,8 @@ SYNTAX: `INSTANCE: SYNTAX: `inline [ word make-inline ] over push-all ; +SYNTAX: `call-next-method T{ fake-call-next-method } parsed ; + : (INTERPOLATE) ( accum quot -- accum ) [ scan interpolate-locals ] dip '[ _ with-string-writer @ ] parsed ; @@ -117,6 +129,7 @@ DEFER: ;FUNCTOR delimiter { "INSTANCE:" POSTPONE: `INSTANCE: } { "SYNTAX:" POSTPONE: `SYNTAX: } { "inline" POSTPONE: `inline } + { "call-next-method" POSTPONE: `call-next-method } } ; : push-functor-words ( -- ) From 84f672e74b3b6cd556e842df0455a444fa5602b9 Mon Sep 17 00:00:00 2001 From: Slava Pestov <slava@slava-pestovs-macbook-pro.local> Date: Sun, 26 Apr 2009 21:24:55 -0500 Subject: [PATCH 14/14] tuple-arrays: further performance improvements --- basis/tuple-arrays/tuple-arrays-tests.factor | 7 +++ basis/tuple-arrays/tuple-arrays.factor | 55 +++++++++++--------- 2 files changed, 37 insertions(+), 25 deletions(-) diff --git a/basis/tuple-arrays/tuple-arrays-tests.factor b/basis/tuple-arrays/tuple-arrays-tests.factor index 4606ecdada..2eeae20aa1 100644 --- a/basis/tuple-arrays/tuple-arrays-tests.factor +++ b/basis/tuple-arrays/tuple-arrays-tests.factor @@ -23,3 +23,10 @@ TUPLE-ARRAY: baz [ 0 ] [ 1 <baz-array> first bing>> ] unit-test [ f ] [ 1 <baz-array> first bong>> ] unit-test + +TUPLE: broken x ; +: broken ( -- ) ; + +TUPLE-ARRAY: broken + +[ 100 ] [ 100 <broken-array> length ] unit-test \ No newline at end of file diff --git a/basis/tuple-arrays/tuple-arrays.factor b/basis/tuple-arrays/tuple-arrays.factor index 466262f3e0..35d771416c 100644 --- a/basis/tuple-arrays/tuple-arrays.factor +++ b/basis/tuple-arrays/tuple-arrays.factor @@ -1,26 +1,36 @@ ! Copyright (C) 2009 Slava Pestov. ! See http://factorcode.org/license.txt for BSD license. -USING: accessors arrays combinators.smart fry functors grouping -kernel macros sequences sequences.private stack-checker -parser ; +USING: accessors arrays combinators.smart fry functors kernel +kernel.private macros sequences combinators sequences.private +stack-checker parser math classes.tuple.private ; FROM: inverse => undo ; IN: tuple-arrays <PRIVATE +MACRO: boa-unsafe ( class -- quot ) tuple-layout '[ _ <tuple-boa> ] ; + MACRO: infer-in ( class -- quot ) infer in>> '[ _ ] ; +: tuple-arity ( class -- quot ) '[ _ boa ] infer-in ; inline + : smart-tuple>array ( tuple class -- array ) '[ [ _ boa ] undo ] output>array ; inline -: smart-array>tuple ( array class -- tuple ) - '[ _ boa ] input<sequence ; inline - -: tuple-arity ( class -- quot ) '[ _ boa ] infer-in ; inline - : tuple-prototype ( class -- array ) [ new ] [ smart-tuple>array ] bi ; inline +: tuple-slice ( n seq -- slice ) + [ n>> [ * dup ] keep + ] [ seq>> ] bi { array } declare slice boa ; inline + +: read-tuple ( slice class -- tuple ) + '[ _ boa-unsafe ] input<sequence-unsafe ; inline + +MACRO: write-tuple ( class -- quot ) + [ '[ [ _ boa ] undo ] ] + [ tuple-arity <reversed> [ '[ [ _ ] dip set-nth-unsafe ] ] map '[ _ cleave ] ] + bi '[ _ dip @ ] ; + PRIVATE> FUNCTOR: define-tuple-array ( CLASS -- ) @@ -35,31 +45,26 @@ CLASS-array? IS ${CLASS-array}? WHERE -TUPLE: CLASS-array { seq sliced-groups read-only } ; +TUPLE: CLASS-array +{ seq array read-only } +{ n array-capacity read-only } +{ length array-capacity read-only } ; : <CLASS-array> ( length -- tuple-array ) - CLASS tuple-prototype <repetition> concat - CLASS tuple-arity <sliced-groups> - CLASS-array boa ; + [ \ CLASS [ tuple-prototype <repetition> concat ] [ tuple-arity ] bi ] keep + \ CLASS-array boa ; inline -M: CLASS-array nth-unsafe - seq>> nth-unsafe CLASS smart-array>tuple ; +M: CLASS-array length length>> ; -M: CLASS-array set-nth-unsafe - [ CLASS smart-tuple>array ] 2dip seq>> set-nth-unsafe ; +M: CLASS-array nth-unsafe tuple-slice \ CLASS read-tuple ; -M: CLASS-array new-sequence - drop <CLASS-array> ; +M: CLASS-array set-nth-unsafe tuple-slice \ CLASS write-tuple ; -: >CLASS-array ( seq -- tuple-array ) - dup empty? [ - 0 <CLASS-array> clone-like - ] unless ; +M: CLASS-array new-sequence drop <CLASS-array> ; -M: CLASS-array like - drop dup CLASS-array? [ >CLASS-array ] unless ; +: >CLASS-array ( seq -- tuple-array ) 0 <CLASS-array> clone-like ; -M: CLASS-array length seq>> length ; +M: CLASS-array like drop dup CLASS-array? [ >CLASS-array ] unless ; INSTANCE: CLASS-array sequence