Paste: cours2

Author: Sam
Mode: factor
Date: Fri, 18 Jun 2010 09:01:03
Plain Text |
IN: inf355.cours2
USING: accessors arrays assocs combinators.smart continuations
       destructors fry infix io kernel locals make math namespaces
       sequences ;

! "call(" and "fry" illustration.

TUPLE: demo quot ;

: call-demo ( demo -- v )
    quot>> call( -- x ) ;

: add-gen ( a b c -- quot )
    [ [ + ] 2curry ]
    [ [ - ] curry ] bi* compose ;

: add-gen2 ( a b c d -- quot )
    '[ _ _ + _ @ ] ;

! Local variables and infix syntax. Most of the time, none of them is
! needed, but in some cases in may make the code clearer (e.g., when
! complex mathematical formulas are involved).

SYMBOL: myvar

:: delta ( a b c -- d )
    b b * a c * 4 * - ;

:: delta2 ( a b c -- d )
    [infix sq(b)-4*a*c infix] ;

:: delta3 ( a b c -- d )
    a b c 3array :> tab
    [infix sq(tab[1])-4*tab[0]*tab[2] infix] ;

:: example1 ( a b -- c d )
    a b :> ( c d ) c d ;

: example2 ( a b -- c )
    [let :> a :> b a b + ] ;

: example3 ( a b -- c )
    [let :> a a + ] ;

: delta4 ( a b c -- d )
    swapd [ sq ] [ 4 * ] [ * - ] tri* ;

! Destructors and object disposal. You can compare what happens when
! you raise an exception in "test-dispose" quotation.

TUPLE: myclass < disposable ;

M: myclass dispose*
    drop "Object disposal" print ;

: test-dispose ( -- obj )
    myclass new-disposable
    [ |dispose "Code execution" print ] with-destructors
    "End of code execution" print
    ;

! Let's implement Haskell like monads in Factor. Note for the readers:
! this has been done live in an interactive class session. This is
! probably not the best way to do it!

MIXIN: monad

! We use a "current-monad" variable to represent the context. It makes
! writing ">>=" easier since the monad won't be on the way to deeper
! stack values.

SYMBOL: current-monad

: with-monad ( monad quot -- monad )
    [ current-monad ] dip [ current-monad get ] compose with-variable ; inline

! The three monadic operations we will use are "fail", "return" and
! ">>=" (bind). Those will call generic words that can be overriden
! for individual monad classes. The current monad isn't used for
! "fail*" and "return*" but is needed to get proper dispatching.

GENERIC: fail* ( monad -- monad )
GENERIC: return* ( data monad -- monad )
GENERIC# >>=* 1 ( monad quot -- monad )

! By default, we may want to have a defaut "fail" behaviour if "fail*" is
! not overriden. However, here we do not want it.

ERROR: failure ;

! M: monad fail* failure ;

: fail ( -- )
    current-monad [ fail* ] change ;

: return ( data -- )
    current-monad [ return* ] change ;

:: >>= ( quot -- )
    current-monad [ quot >>=* ] change ;

! Data used for testing.

: telsdata ( -- hash )
    H{ { "Sam" "0661" }
       { "Julien" "0991" } } ;

: tels ( name -- )
    telsdata at [ return ] [ fail ] if* ;

! Implementation of the "maybe" tuple, corresponding to Haskell's maybe.
! Example use:
!
!   ( scratchpad ) "Julien" just [ [ tels ] >>= [ "Phone number is " prepend return ] >>= ] with-monad .
!   T{ maybe { set t } { data "Phone number is 0991" } }
!
!   ( scratchpad ) nothing [ [ tels ] >>= [ "Phone number is " prepend return ] >>= ] with-monad .
!   T{ maybe }
!
!   ( scratchpad ) "Claire" just [ [ tels ] >>= [ "Phone number is " prepend return ] >>= ] with-monad .
!   T{ maybe }


TUPLE: maybe
    { set boolean initial: f }
    data ;

: just ( data -- maybe )
    [ maybe new t >>set ] dip >>data ;

: nothing ( -- maybe )
    maybe new ;

: from-maybe ( maybe -- data )
    dup set>> t assert=
    data>> ;

INSTANCE: maybe monad

M: maybe fail*
    drop nothing ;

M: maybe return*
    drop just ;

M: maybe >>=*
    over set>>
    [
        [ from-maybe ] [ call( data -- ) ] bi* current-monad get
    ] [
        drop
    ] if ;

! A sequence may be a monad. Example use with the very same code:
!
!   ( scratchpad ) { "Julien" } [ [ tels ] >>= [ "Phone number is " prepend return ] >>= ] with-monad .
!   { "Phone number is 0991" }
!
!   ( scratchpad ) { "Claire" } [ [ tels ] >>= [ "Phone number is " prepend return ] >>= ] with-monad .
!   { }
!
!   ( scratchpad ) { "Julien" "Sam" } [ [ tels ] >>= [ "Phone number is " prepend return ] >>= ] with-monad .
!   { "Phone number is 0991" "Phone number is 0661" }

INSTANCE: sequence monad

M: sequence fail*
      { } swap like ;

M: sequence return*
    [ 1array ] [ like ] bi* ;

M: sequence >>=*
    [ call( x -- ) current-monad get ] curry map concat ;

! We could also say that any object is a monad, with "f" representing a failure. For that to work
! properly and not interact with sequences, we need to either comment out the "sequence is a monad"
! section or specialize it for only some kind of sequences.

! INSTANCE: object monad
!
! M: object fail* drop f ;
!
! M: object return* drop ;
!
! M: object >>=*
!     over [ call( x -- ) current-monad get ] [ drop ] if ;

! We can implement "do" which will bind every quotation in a sequence and return the result. Example use:
!
!  ( scratchpad ) { "Julien" "Sam" } { [ tels ] [ "Phone number is " prepend return ] } do .
!  { "Phone number is 0991" "Phone number is 0661" }

: do ( monad seq -- monad )
    [ [ >>= ] each ] curry with-monad ;

! "do*" will also execute each quotation, and "fail" if an exception is raised or "return" it if it
! doesn't. Example use:
!
!  ( scratchpad ) { 1 0 2 } { [ 1 swap / ] [ 2 + ] } do* .
!  { 3 2+1/2 }
!
!  ( scratchpad ) 1 just { [ 1 swap / ] [ 2 + ] } do* .
!  T{ maybe { set t } { data 3 } }
!
!  ( scratchpad ) 0 just { [ 1 swap / ] [ 2 + ] } do* .
!  T{ maybe }

: do* ( monad seq -- monad )
    [ '[ [ @ return ] [ 2drop fail ] recover ] ] map do ;

New Annotation

Summary:
Author:
Mode:
Body: