535 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			Factor
		
	
	
			
		
		
	
	
			535 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			Factor
		
	
	
| ! Copyright (C) 2009 Doug Coleman.
 | |
| ! See http://factorcode.org/license.txt for BSD license.
 | |
| USING: assocs combinators constructors eval help.markup kernel
 | |
| multiline namespaces parser sequences sequences.private slides
 | |
| vocabs.refresh words fry ;
 | |
| IN: tc-lisp-talk
 | |
| 
 | |
| CONSTANT: tc-lisp-slides
 | |
| {
 | |
|     { $slide "Factor!"
 | |
|         { $url "http://factorcode.org" }
 | |
|         "Development started in 2003"
 | |
|         "Open source (BSD license)"
 | |
|         "Influenced by Forth, Lisp, and Smalltalk"
 | |
|         "Blurs the line between language and library"
 | |
|         "Interactive development"
 | |
|     }
 | |
|     { $slide "First, some examples"
 | |
|         { $code "3 weeks ago noon monday ." }
 | |
|         { $code "USE: roman 2009 >roman ." }
 | |
|         { $code """: average ( seq -- x )
 | |
|     [ sum ] [ length ] bi / ;""" }
 | |
|         { $code "1 miles [ km ] undo >float ." }
 | |
|         { $code "[ readln eval>string print t ] loop" }
 | |
|     }
 | |
|     { $slide "XML Literals"
 | |
|         { $code
 | |
|         """USING: splitting xml.writer xml.syntax ;
 | |
| { "one" "two" "three" } 
 | |
| [ [XML <item><-></item> XML] ] map
 | |
| <XML <doc><-></doc> XML> pprint-xml"""
 | |
|         }
 | |
|     }
 | |
|     { $slide "Differences between Factor and Lisp"
 | |
|         "Single-implementation language"
 | |
|         "Less nesting, shorter word length"
 | |
|         { "Dynamic reloading of code from files with " { $link refresh-all } }
 | |
|         "More generic protocols -- sequences, assocs, streams"
 | |
|         "More cross-platform"
 | |
|         "No standard for the language"
 | |
|         "Evaluates left to right"
 | |
|     }
 | |
|     { $slide "Terminology"
 | |
|         { "Words - functions" }
 | |
|         { "Vocabularies - collections of code in the same namespace" }
 | |
|         { "Quotations - blocks of code" { $code "[ dup reverse append ]" } }
 | |
|         { "Combinators - higher order functions" }
 | |
|         { "Static stack effect - known stack effect at compile-time" }
 | |
|     }
 | |
|     { $slide "Defining a word"
 | |
|         "Defined at parse time"
 | |
|         "Parts: name, stack effect, definition"
 | |
|         "Composed of tokens separated by whitespace"
 | |
|         { $code ": palindrome? ( string -- ? ) dup reverse = ;" }
 | |
|     }
 | |
|     { $slide "Non-static stack effect"
 | |
|         "Not a good practice, nor useful"
 | |
|         "Not compiled by the optimizing compiler"
 | |
|         { $code "100 iota [ ] each" }
 | |
|     }
 | |
|     { $slide "Module system"
 | |
|         "Code divided up into vocabulary roots"
 | |
|         "core/ -- just enough code to bootstrap Factor"
 | |
|         "basis/ -- optimizing compiler, the UI, tools, libraries"
 | |
|         "extra/ -- demos, unpolished code, experiments"
 | |
|         "work/ -- your works in progress"
 | |
|     }
 | |
|     { $slide "Module system (part 2)"
 | |
|         "Each vocabulary corresponds to a directory on disk, with documentation and test files"
 | |
|         { "Code for the " { $snippet "math" } " vocabulary: " { $snippet "~/factor/core/math/math.factor" } }
 | |
|         { "Documentation for the " { $snippet "math" } " vocabulary: " { $snippet "~/factor/core/math/math-docs.factor" } }
 | |
|         { "Unit tests for the " { $snippet "math" } " vocabulary: " { $snippet " ~/factor/core/math/math-tests.factor" } }
 | |
|     }
 | |
|     { $slide "Using a library"
 | |
|         "Each file starts with a USING: list"
 | |
|         "To use a library, simply include it in this list"
 | |
|         "Refreshing code loads dependencies correctly"
 | |
|     }
 | |
|     { $slide "Object system"
 | |
|         "Based on CLOS"
 | |
|         { "We define generic words that operate on the top of the stack with " { $link POSTPONE: GENERIC:  } " or on an implicit parameter with " { $link POSTPONE: HOOK: } }
 | |
|     }
 | |
|     { $slide "Object system example: shape protocol"
 | |
|         "In ~/factor/work/shapes/shapes.factor"
 | |
|         { $code """IN: shapes
 | |
| 
 | |
| GENERIC: area ( shape -- x )
 | |
| GENERIC: perimeter ( shape -- x )"""
 | |
|         }
 | |
|     }
 | |
|     { $slide "Implementing the shape protocol: circles"
 | |
|         "In ~/factor/work/shapes/circle/circle.factor"
 | |
|         { $code """USING: shapes constructors math
 | |
| math.constants ;
 | |
| IN: shapes.circle
 | |
| 
 | |
| TUPLE: circle radius ;
 | |
| CONSTRUCTOR: circle ( radius -- obj ) ;
 | |
| M: circle area radius>> sq pi * ;
 | |
| M: circle perimeter radius>> pi * 2 * ;"""
 | |
|         }
 | |
|     }
 | |
|     { $slide "Dynamic variables"
 | |
|         "Implemented as a stack of hashtables"
 | |
|         { "Useful words are " { $link get } ", " { $link set } }
 | |
|         "Input, output, error streams are stored in dynamic variables"
 | |
|         { $code """"Today is the first day of the rest of your life."
 | |
| [
 | |
|     readln print
 | |
| ] with-string-reader"""
 | |
|         }
 | |
|     }
 | |
|     { $slide "The global namespace"
 | |
|         "The global namespace is just the namespace at the bottom of the namespace stack"
 | |
|         { "Useful words are " { $link get-global } ", " { $link set-global } }
 | |
|         "Factor idiom for changing a particular namespace"
 | |
|         { $code """SYMBOL: king
 | |
| global [ "Henry VIII" king set ] bind"""
 | |
|         }
 | |
|         { $code "with-scope" }
 | |
|         { $code "namestack" }
 | |
|     }
 | |
|     { $slide "Hooks"
 | |
|         "Dispatch on a dynamic variable"
 | |
|         { $code """HOOK: computer-name os ( -- string )
 | |
| M: macosx computer-name uname first ;
 | |
| macosx \ os set-global
 | |
| computer-name"""
 | |
|         }
 | |
|     }
 | |
|     { $slide "Interpolate"
 | |
|         "Replaces variables in a string"
 | |
|         { $code
 | |
| """"Dawg" "name" set
 | |
| "rims" "noun" set
 | |
| "bling" "verb1" set
 | |
| "roll" "verb2" set
 | |
| [
 | |
|     "Sup ${name}, we heard you liked ${noun}, so we put ${noun} on your car so you can ${verb1} while you ${verb2}."
 | |
|     interpolate
 | |
| ] with-string-writer print """
 | |
|         }
 | |
|     }
 | |
|     { $slide "Sequence protocol"
 | |
|         "All sequences obey a protocol of generics"
 | |
|         { "Is an object a " { $link sequence? } }
 | |
|         { "Getting the " { $link length } }
 | |
|         { "Accessing the " { $link nth  } " element" }
 | |
|         { "Setting an element - " { $link set-nth } }
 | |
|     }
 | |
|     { $slide "Examples of sequences in Factor"
 | |
|         "Arrays are mutable"
 | |
|         "Vectors are mutable and growable"
 | |
|         { "Arrays " { $code "{ \"abc\" \"def\" 50 }" } }
 | |
|         { "Vectors " { $code "V{ \"abc\" \"def\" 50 }" } }
 | |
|         { "Byte-arrays " { $code "B{ 1 2 3 }" } }
 | |
|         { "Byte-vectors " { $code "BV{ 11 22 33 }" } }
 | |
|     }
 | |
|     { $slide "Specialized arrays and vectors"
 | |
|         { "Specialized int arrays " { $code "int-array{ -20 -30 40 }" } }
 | |
|         { "Specialized uint arrays " { $code "uint-array{ 20 30 40 }" } }
 | |
|         { "Specialized float vectors " { $code "float-vector{ 20 30 40 }" } }
 | |
|         "35 others C-type arrays"
 | |
|     }
 | |
|     { $slide "Specialized arrays code"
 | |
|         "One line per array/vector"
 | |
|         { "In ~/factor/basis/specialized-arrays/float/float.factor"
 | |
|             { $code """<< "float" define-array >>""" }
 | |
|         }
 | |
|         { "In ~/factor/basis/specialized-vectors/float/float.factor"
 | |
|             { $code """<< "float" define-vector >>""" }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     { $slide "Speciailzied arrays are implemented using functors"
 | |
|         "Like C++ templates"
 | |
|         "Eliminate boilerplate in ways other abstractions don't"
 | |
|         "Contains a definition section and a functor body"
 | |
|         "Uses the interpolate vocabulary"
 | |
|     }
 | |
|     { $slide "Functor for sorting"
 | |
|         { $code
 | |
|             """FUNCTOR: define-sorting ( NAME QUOT -- )
 | |
| 
 | |
| NAME<=> DEFINES ${NAME}<=>
 | |
| NAME>=< DEFINES ${NAME}>=<
 | |
| 
 | |
| WHERE
 | |
| 
 | |
| : NAME<=> ( obj1 obj2 -- <=> ) QUOT compare ;
 | |
| : NAME>=< ( obj1 obj2 -- >=< )
 | |
|     NAME<=> invert-comparison ;
 | |
| 
 | |
| ;FUNCTOR"""
 | |
|         }
 | |
|     }
 | |
|     { $slide "Example of sorting functor"
 | |
|         { $code """USING: sorting.functor ;
 | |
| << "length" [ length ] define-sorting >>"""
 | |
|         }
 | |
|         { $code
 | |
|             """{ { 1 2 3 } { 1 2 } { 1 } }
 | |
| [ length<=> ] sort"""
 | |
|         }
 | |
|     }
 | |
|     { $slide "Combinators"
 | |
|         "Used to implement higher order functions (dataflow and control flow)"
 | |
|         "Compiler optimizes away quotations completely"
 | |
|         "Optimized code is just tight loops in registers"
 | |
|         "Most loops can be expressed with combinators or tail-recursion"
 | |
|     }
 | |
|     { $slide "Combinators that act on one value"
 | |
|         { $link bi }
 | |
|         { $code "10 [ 1 - ] [ 1 + ] bi" }
 | |
|         { $link tri }
 | |
|         { $code "10 [ 1 - ] [ 1 + ] [ 2 * ] tri" }
 | |
|     }
 | |
|     { $slide "Combinators that act on two values"
 | |
|         { $link 2bi }
 | |
|         { $code "10 1 [ - ] [ + ] 2bi" }
 | |
|         { $link bi* }
 | |
|         { $code "10 20 [ 1 - ] [ 1 + ] bi*" }
 | |
|         { $link bi@ }
 | |
|         { $code "5 9 [ sq ] bi@" }
 | |
|     }
 | |
|     { $slide "Sequence combinators"
 | |
|         
 | |
|         { $link each }
 | |
|         { $code "{ 1 2 3 4 5 } [ sq . ] each" }
 | |
|         { $link map }
 | |
|         { $code "{ 1 2 3 4 5 } [ sq ] map" }
 | |
|         { $link filter }
 | |
|         { $code "{ 1 2 3 4 5 } [ even? ] filter" }
 | |
|     }
 | |
|     { $slide "Multiple sequence combinators"
 | |
|         
 | |
|         { $link 2each }
 | |
|         { $code "{ 1 2 3 } { 10 20 30 } [ + . ] 2each" }
 | |
|         { $link 2map }
 | |
|         { $code "{ 1 2 3 } { 10 20 30 } [ + ] 2map" }
 | |
|     }
 | |
|     { $slide "Control flow: if"
 | |
|         { $link if }
 | |
|         { $code """10 random dup even? [ 2 / ] [ 1 - ] if""" }
 | |
|         { $link when }
 | |
|         { $code """10 random dup even? [ 2 / ] when""" }
 | |
|         { $link unless }
 | |
|         { $code """10 random dup even? [ 1 - ] unless""" }
 | |
|     }
 | |
|     { $slide "Control flow: case"
 | |
|         { $link case }
 | |
|         { $code """ERROR: not-possible obj ;
 | |
| 10 random 5 <=> {
 | |
|     { +lt+ [ "Less" ] }
 | |
|     { +gt+ [ "More" ] }
 | |
|     { +eq+ [ "Equal" ] }
 | |
|     [ not-possible ]
 | |
| } case"""
 | |
|         }
 | |
|     }
 | |
|     { $slide "Fry"
 | |
|         "Used to construct quotations"
 | |
|         { "'Holes', represented by " { $snippet "_" } " are filled left to right" }
 | |
|         { $code "10 4 '[ _ + ] call" }
 | |
|         { $code "3 4 '[ _ sq _ + ] call" }
 | |
|     }
 | |
|     { $slide "Locals"
 | |
|         "When data flow combinators and shuffle words are not enough"
 | |
|         "Name your input parameters"
 | |
|         "Used in about 1% of all words"
 | |
|     }
 | |
|     { $slide "Locals example"
 | |
|         "Area of a triangle using Heron's formula"
 | |
|         { $code
 | |
|             """:: area ( a b c -- x )
 | |
|     a b c + + 2 / :> p
 | |
|     p
 | |
|     p a - *
 | |
|     p b - *
 | |
|     p c - * sqrt ;"""
 | |
|         }
 | |
|     }
 | |
|     { $slide "Previous example without locals"
 | |
|         "A bit unwieldy..."
 | |
|         { $code
 | |
|             """: area ( a b c -- x )
 | |
|     [ ] [ + + 2 / ] 3bi
 | |
|     [ '[ _ - ] tri@ ] [ neg ] bi
 | |
|     * * * sqrt ;""" }
 | |
|     }
 | |
|     { $slide "More idiomatic version"
 | |
|         "But there's a trick: put the lengths in an array"
 | |
|         { $code """: v-n ( v n -- w ) '[ _ - ] map ;
 | |
| 
 | |
| : area ( seq -- x )
 | |
|     [ 0 suffix ] [ sum 2 / ] bi
 | |
|     v-n product sqrt ;""" }
 | |
|     }
 | |
|     { $slide "Implementing an abstraction"
 | |
|         { "Suppose we want to get the price of the customer's first order, but any one of the steps along the way could be a nil value (" { $link f } " in Factor):" }
 | |
|         { $code
 | |
|             "dup [ orders>> ] when"
 | |
|             "dup [ first ] when"
 | |
|             "dup [ price>> ] when"
 | |
|         }
 | |
|     }
 | |
|     { $slide "This is hard with mainstream syntax!"
 | |
|         { $code
 | |
|             """var customer = ...;
 | |
| var orders = (customer == null ? null : customer.orders);
 | |
| var order = (orders == null ? null : orders[0]);
 | |
| var price = (order == null ? null : order.price);""" }
 | |
|     }
 | |
|     { $slide "An ad-hoc solution"
 | |
|         "Something like..."
 | |
|         { $code "var price = customer.?orders.?[0].?price;" }
 | |
|     }
 | |
|     { $slide "Macros in Factor"
 | |
|         "Expand at compile-time"
 | |
|         "Return a quotation to be compiled"
 | |
|         "Can express non-static stack effects"
 | |
|         "Not as widely used as combinators, 60 macros so far"
 | |
|         { $code "{ 1 2 3 4 5 } 5 firstn" }
 | |
|     }
 | |
|     { $slide "A macro solution"
 | |
|         "Returns a quotation to the compiler"
 | |
|         "Constructed using map, fry, and concat"
 | |
|         { $code """MACRO: plox ( seq -- quot )
 | |
|     [
 | |
|         '[ dup _ when ]
 | |
|     ] map [ ] concat-as ;"""
 | |
|         }
 | |
|     }
 | |
|     { $slide "Macro example"
 | |
|         "Return the caaar of a sequence"
 | |
|         { "Return " { $snippet f } " on failure" }
 | |
|         { $code """: caaar ( seq/f -- x/f )
 | |
|     {
 | |
|         [ first ]
 | |
|         [ first ]
 | |
|         [ first ]
 | |
|     } plox ;"""
 | |
|         }
 | |
|         { $code """{ { f } } caaar""" }
 | |
|         { $code """{ { { 1 2 3 } } } caaar""" }
 | |
|     }
 | |
|     { $slide "Smart combinators"
 | |
|         "Use stack checker to infer inputs and outputs"
 | |
|         "Even fewer uses than macros"
 | |
|         { $code "{ 1 10 20 34 } sum" }
 | |
|         { $code "[ 1 10 20 34 ] sum-outputs" }
 | |
|         { $code "[ 2 2 [ even? ] both? ] [ + ] [ - ] smart-if" }
 | |
|     }
 | |
|     { $slide "Fibonacci"
 | |
|         "Not tail recursive"
 | |
|         "Call tree is huge"
 | |
|         { $code """: fib ( n -- x )
 | |
|     dup 1 <= [
 | |
|         [ 1 - fib ] [ 2 - fib ] bi +
 | |
|     ] unless ;"""
 | |
|         }
 | |
|         { $code "36 iota [ fib ] map ." }
 | |
|     }
 | |
|     { $slide "Memoized Fibonacci"
 | |
|         "Change one word and it's efficient"
 | |
|         { $code """MEMO: fib ( n -- x )
 | |
|     dup 1 <= [
 | |
|         [ 1 - fib ] [ 2 - fib ] bi +
 | |
|     ] unless ;"""
 | |
|         }
 | |
|         { $code "36 iota [ fib ] map ." }
 | |
|     }
 | |
|     { $slide "Destructors"
 | |
|         "Deterministic resource disposal"
 | |
|         "Any step can fail and we don't want to leak resources"
 | |
|         "We want to conditionally clean up sometimes -- if everything succeeds, we might wish to retain the buffer"
 | |
|     }
 | |
| 
 | |
|     { $slide "Example in C"
 | |
|         { $code
 | |
| """void do_stuff()
 | |
| {
 | |
|     void *obj1, *obj2;
 | |
|     if(!(*obj1 = malloc(256))) goto end;
 | |
|     if(!(*obj2 = malloc(256))) goto cleanup1;
 | |
|     ... work goes here...
 | |
| cleanup2: free(*obj2);
 | |
| cleanup1: free(*obj1);
 | |
| end: return;
 | |
| }"""
 | |
|     }
 | |
|     }
 | |
|     { $slide "Example: allocating and disposing two buffers"
 | |
|         { $code """: do-stuff ( -- )
 | |
|     [
 | |
|         256 malloc &free
 | |
|         256 malloc &free
 | |
|         ... work goes here ...
 | |
|     ] with-destructors ;"""
 | |
|         }
 | |
|     }
 | |
|     { $slide "Example: allocating two buffers for later"
 | |
|         { $code """: do-stuff ( -- )
 | |
|     [
 | |
|         256 malloc |free
 | |
|         256 malloc |free
 | |
|         ... work goes here ...
 | |
|     ] with-destructors ;"""
 | |
|         }
 | |
|     }
 | |
|     { $slide "Example: disposing of an output port"
 | |
|         { $code """M: output-port dispose*
 | |
|     [
 | |
|         {
 | |
|             [ handle>> &dispose drop ]
 | |
|             [ buffer>> &dispose drop ]
 | |
|             [ port-flush ]
 | |
|             [ handle>> shutdown ]
 | |
|         } cleave
 | |
|     ] with-destructors ;"""
 | |
|         }
 | |
|     }
 | |
|     { $slide "Rapid application development"
 | |
|         "We lost the dice to Settlers of Catan: Cities and Knights"
 | |
|         "Two regular dice, one special die"
 | |
|         { $vocab-link "dice" }
 | |
|     }
 | |
|     { $slide "The essence of Factor"
 | |
|         "Nicely named words abstract away the stack, leaving readable code"
 | |
|         { $code """: surround ( seq left right -- seq' )
 | |
|     swapd 3append ;"""
 | |
|         }
 | |
|         { $code """: glue ( left right middle -- seq' )
 | |
|     swap 3append ;"""
 | |
|         }
 | |
|         { $code HEREDOC: xyz
 | |
| "a" "b" "c" 3append
 | |
| "a" """""""" surround
 | |
| "a" "b" ", " glue
 | |
| xyz
 | |
|         }
 | |
|     }
 | |
|     { $slide "C FFI demo"
 | |
|         "Easy to call C functions from Factor"
 | |
|         "Handles C structures, C types, callbacks"
 | |
|         "Used extensively in the Windows and Unix backends"
 | |
|         { $code
 | |
|             """FUNCTION: double pow ( double x, double y ) ;
 | |
| 2 5.0 pow ."""
 | |
|         }
 | |
|     }
 | |
|     { $slide "Windows win32 example"
 | |
|         { $code
 | |
| """M: windows gmt-offset
 | |
|     ( -- hours minutes seconds )
 | |
|     "TIME_ZONE_INFORMATION" <c-object>
 | |
|     dup GetTimeZoneInformation {
 | |
|         { TIME_ZONE_ID_INVALID [
 | |
|             win32-error-string throw
 | |
|         ] }
 | |
|         { TIME_ZONE_ID_STANDARD [
 | |
|             TIME_ZONE_INFORMATION-Bias
 | |
|         ] }
 | |
|     } case neg 60 /mod 0 ;"""
 | |
|         }
 | |
|     }
 | |
|     { $slide "Struct and function"
 | |
|         { $code """C-STRUCT: TIME_ZONE_INFORMATION
 | |
|     { "LONG" "Bias" }
 | |
|     { { "WCHAR" 32 } "StandardName" }
 | |
|     { "SYSTEMTIME" "StandardDate" }
 | |
|     { "LONG" "StandardBias" }
 | |
|     { { "WCHAR" 32 } "DaylightName" }
 | |
|     { "SYSTEMTIME" "DaylightDate" }
 | |
|     { "LONG" "DaylightBias" } ;"""
 | |
|         }
 | |
|         { $code """FUNCTION: DWORD GetTimeZoneInformation (
 | |
|     LPTIME_ZONE_INFORMATION
 | |
|         lpTimeZoneInformation
 | |
| ) ;"""
 | |
|         }
 | |
| 
 | |
|     }
 | |
|     { $slide "Cocoa FFI"
 | |
|         { $code """IMPORT: NSAlert [
 | |
|     NSAlert -> new
 | |
|     [ -> retain ] [
 | |
|         "Raptor" <CFString> &CFRelease
 | |
|         -> setMessageText:
 | |
|     ] [
 | |
|         "Look out!" <CFString> &CFRelease
 | |
|         -> setInformativeText:
 | |
|     ] tri -> runModal drop
 | |
| ] with-destructors"""
 | |
|         }
 | |
|     }
 | |
|     { $slide "Deployment demo"
 | |
|         "Vocabularies can be deployed"
 | |
|         "Standalone .app on Mac"
 | |
|         "An executable and dll on Windows"
 | |
|         { $vocab-link "webkit-demo" }
 | |
|     }
 | |
|     { $slide "Interesting programs"
 | |
|         { $vocab-link "terrain" }
 | |
|         { $vocab-link "gpu.demos.raytrace" }
 | |
|         { $vocab-link "gpu.demos.bunny" }
 | |
|     }
 | |
|     { $slide "Factor's source tree"
 | |
|         "Lines of code in core/: 9,500"
 | |
|         "Lines of code in basis/: 120,000"
 | |
|         "Lines of code in extra/: 51,000"
 | |
|         "Lines of tests: 44,000"
 | |
|         "Lines of documentation: 44,500"
 | |
|     }
 | |
|     { $slide "VM trivia"
 | |
|         "Lines of C++ code: 12860"
 | |
|         "Generational garbage collection"
 | |
|         "Non-optimizing compiler"
 | |
|         "Loads an image file and runs it"
 | |
|     }
 | |
|     { $slide "Why should I use Factor?"
 | |
|         "More abstractions over time"
 | |
|         "We fix reported bugs quickly"
 | |
|         "Stackable, fluent language"
 | |
|         "Supports extreme programming"
 | |
|         "Beer-friendly programming"
 | |
|     }
 | |
|     { $slide "Questions?"
 | |
|     }
 | |
| }
 | |
| 
 | |
| : tc-lisp-talk ( -- ) tc-lisp-slides slides-window ;
 | |
| 
 | |
| MAIN: tc-lisp-talk
 |