Sztuczki w programowaniu
Transkrypt
Sztuczki w programowaniu
Sztuczki w programowaniu Sztuczki w programowaniu Cezary Bartoszuk 9 listopada 2010 Sztuczki w programowaniu Wstęp Ja Cezary Bartoszuk Info: student MIMUW zawodowo programista Pythona Zainteresowania: Języki programowania Testowanie Programowanie funkcyjne ... Sztuczki w programowaniu Wstęp Plan Co będzie? Funkcje wyższych rzędów Monady Typy wyższych rzędów Klasy abstrakcji na typach Kontynuacje Sztuczki w programowaniu Przepływ danych Funkcje wyższych rzędów Funkcje wyższych rzędów Funkcje wyższych rzędów a raczej obywatele 1. kategorii Sztuczki w programowaniu Przepływ danych Funkcje wyższych rzędów Funkcje wyższych rzędów Example def id [ A ]( a : A ) = a // id : [ A ]( a : A ) A Example def compose [A , B , C ]( inner : A = > B )( outer : B = > C ) = ( a : A ) = > outer ( inner ( a )) // compose : [A ,B , C ]( inner : ( A ) = > B )( outer : ( B ) = > C )( A ) = > C Sztuczki w programowaniu Przepływ danych Funkcje wyższych rzędów Przykład: Programming Clojure Example ( defn blank ? [ s ] ( every ? # ( Character / isWhitespace %) s )) Example public class StringUtils { public static boolean isBlank ( String str ) { int strLen ; if ( str == null || ( strLen = str . length ()) == 0) { return true ; } for ( int i = 0; i < strLen ; i ++) { if ( Character . isWhitespace ( str . charAt ( i )) == false ) { return false ; } } return true ; } } Sztuczki w programowaniu Przepływ danych Funkcje wyższych rzędów Przykład Theorem isBlank(s : String ) ⇐⇒ ∀c∈s isWhitespace(c) Example ( defn blank ? [ s ] ( every ? # ( Character / isWhitespace %) s )) Example def isBlank ( s : String ) = s forall Character . isWhitespace // isBlank : ( s : String ) Boolean Sztuczki w programowaniu Przepływ danych Funkcje wyższych rzędów Praktyczny use-case Nadanie własności metodzie/klasie. . . Sztuczki w programowaniu Przepływ danych Funkcje wyższych rzędów Praktyczny use-case Nadanie własności metodzie/klasie. . . monitor transakcja logowanie aplikacja funkcji do wyniku aplikacja szablonu do danych Sztuczki w programowaniu Przepływ danych Funkcje wyższych rzędów Dekoratory w Pythonie Dekoratory! Sztuczki w programowaniu Przepływ danych Funkcje wyższych rzędów Szablony w aplikacji webowej Example class E x a m p l e C o n t r o l l e r ( B a s e R o u t i n g C o n t r o l l e r ): @ expose ( ’ e x a m p l e _ t em p l a t e . html ’) def example ( self ): data = ... return { ’ content ’: data } def expose ( fn , template_path ): def wrapped (* args , ** kwargs ): result = fn (* args , ** kwargs ) renderer = Renderer . fromTemplate ( template_path ) return renderer . render ( result ) return wrapped Sztuczki w programowaniu Przepływ danych Funkcje wyższych rzędów Generyczne funkcje wyższych rzędów Jakie są przykłady ogólnie stosowanych funkcji wyższych rzędów? Sztuczki w programowaniu Przepływ danych Funkcje wyższych rzędów Map f: map f Sztuczki w programowaniu Przepływ danych Funkcje wyższych rzędów Filter p: filter p Bool Sztuczki w programowaniu Przepływ danych Funkcje wyższych rzędów Filter p T F T F F p: filter p Bool Sztuczki w programowaniu Przepływ danych Funkcje wyższych rzędów Fold f f f: x f f f Sztuczki w programowaniu Przepływ danych Funkcje wyższych rzędów Fold f: f x Sztuczki w programowaniu Przepływ danych Funkcje wyższych rzędów Fold f: f f( , )= x Sztuczki w programowaniu Przepływ danych Funkcje wyższych rzędów Fold f: f f f( , )= x Sztuczki w programowaniu Przepływ danych Funkcje wyższych rzędów Fold f: f f f( f , )= x Sztuczki w programowaniu Przepływ danych Funkcje wyższych rzędów Fold f f f( , f: x f f )= Sztuczki w programowaniu Przepływ danych Funkcje wyższych rzędów Fold f f f( , f: x f f )= f Sztuczki w programowaniu Przepływ danych Funkcje wyższych rzędów Implementacja Example def foldLeft [E , A ] ( acc : A ) ( list : List [ E ]) ( f : (A , E ) = > A ): A = list match { case Nil = > acc case head :: tail = > foldLeft ( f ( acc , head )) ( tail ) ( f ) } // foldLeft : [E , A ]( f : (A , E ) = > A )( acc : A )( list : List [ E ]) A Sztuczki w programowaniu Przepływ danych Funkcje wyższych rzędów Implementacja Example def reverse [ A ] ( list : List [ A ]): List [ A ] = foldLeft ( Nil : List [ A ]) ( list ) { ( reversed : List [ A ] , current : A ) = > current :: reversed } // reverse : [ A ]( list : List [ A ]) List [ A ] Sztuczki w programowaniu Przepływ danych Funkcje wyższych rzędów Implementacja Example def filter [ A ] ( list : List [ A ]) ( p : A = > Boolean ) = reverse { foldLeft ( Nil : List [ A ]) ( list ) { ( filtered : List [ A ] , current : A ) = > if ( p ( current )) { current :: filtered } else { filtered } } } // filter : [ A ]( list : List [ A ])( p : ( A ) = > Boolean ) List [ A ] Sztuczki w programowaniu Przepływ danych Funkcje wyższych rzędów Implementacja Example def map [A , B ] ( list : List [ A ]) ( f : A = > B ): List [ B ] = reverse { foldLeft ( Nil : List [ B ]) ( list ) { ( computed : List [ B ] , current : A ) = > f ( current ) :: computed } } // map : [A , B ]( list : List [ A ])( f : ( A ) = > B ) List [ B ] Sztuczki w programowaniu Przepływ danych Funkcje wyższych rzędów Doskonała metoda na zmniejszenie ilości branchy? Example ( defn blank ? [ s ] ( every ? # ( Character / isWhitespace %) s )) Example public class StringUtils { public static boolean isBlank ( String str ) { int strLen ; if ( str == null || ( strLen = str . length ()) == 0) { return true ; } for ( int i = 0; i < strLen ; i ++) { if ( Character . isWhitespace ( str . charAt ( i )) == false ) { return false ; } } return true ; } } Sztuczki w programowaniu Przepływ danych Funkcje wyższych rzędów Wnioski Funkje wyższych rzędów Mogą zredukować ilość gałęzi wykonania kodu Często prościej wyrazić to o co chodzi Dekoratory są super! Sztuczki w programowaniu Przepływ danych Monady Monady Monady Sztuczki w programowaniu Przepływ danych Monady Monady (return x) = f = f x m = return = m (m = f ) = g = m = (λ x → f x = g ) Sztuczki w programowaniu Przepływ danych Monady Obliczenie g f h j i Sztuczki w programowaniu Przepływ danych Monady Obliczenie g f h j i Sztuczki w programowaniu Przepływ danych Monady Obliczenie g f h j i Sztuczki w programowaniu Przepływ danych Monady Obliczenie g f h j i Sztuczki w programowaniu Przepływ danych Monady Obliczenie g f h j i Sztuczki w programowaniu Przepływ danych Monady Obliczenie g f h j i Sztuczki w programowaniu Przepływ danych Monady Obliczenie g f h j i Sztuczki w programowaniu Przepływ danych Monady Obliczenie g f h ??? j ??? i Sztuczki w programowaniu Przepływ danych Monady Problem? Example h: Data → Data Otypowanie funkcji wyraża przekształcenie z Data w Data. Sztuczki w programowaniu Przepływ danych Monady Problem? Example h: Data → Data Otypowanie funkcji wyraża przekształcenie z Data w Data. Example h: Data → Maybe[Data] Teraz typy wyrażają to, że obliczenie może się nie powieźć. Sztuczki w programowaniu Przepływ danych Monady Gdzie tu monady? Monady są abstrakcją obliczenia. Maybe abstrakcja niepowodzenia obliczenia Collection abstrakcja wielu możliwych wyników Error abstracja wyjątku State abstracja modyfikacji stanu podczas obliczeń Continuation abstrakcja zawieszonego obliczenia Sztuczki w programowaniu Przepływ danych Monady Wiki a monad is a kind of abstract data type constructor used to represent computations (instead of data in the domain model). Monads allow the programmer to chain actions together to build a pipeline, in which each action is decorated with additional processing rules provided by the monad. Wikipedia Sztuczki w programowaniu Przepływ danych Monady Uporządkowana trójka? M konstruktor typów return : T → M [T ] = : M [T ] → (T → M [U]) → M [U] Sztuczki w programowaniu Przepływ danych Monady Wnioski Monady Zwiększają wygodę programisty języka funkcyjnego Stanowią pewien model Są trudne do zrozumienia Sztuczki w programowaniu Typy Systemy typowania Typy wyższych rzędów Typy wyższych rzędów Higher-Kinds Sztuczki w programowaniu Typy Systemy typowania ? Czym jest system typów? Sztuczki w programowaniu Typy Systemy typowania ? Czym jest system typów? A type system is a tractable syntactic method for proving the absence of certain program behaviors by classifying phrases according to kinds of values they compute. Benjamin Pierce Sztuczki w programowaniu Typy Systemy typowania ? Czym jest system typów? A type system is a tractable syntactic method for proving the absence of certain program behaviors by classifying phrases according to kinds of values they compute. Benjamin Pierce Sztuczki w programowaniu Typy Typy wyższych rzędów Piramida typów ??? Types Values Sztuczki w programowaniu Typy Typy wyższych rzędów Piramida typów Kinds Types Values Sztuczki w programowaniu Typy Typy wyższych rzędów Nieciekawy przykład Example type type type type Int :: * String :: * ( Int = > String ):: * List [ Int ]:: * Sztuczki w programowaniu Typy Typy wyższych rzędów Nieciekawy przykład Example type type type type type type Int :: * String :: * ( Int = > String ):: * List [ Int ]:: * List :: ??? Function1 :: ??? Sztuczki w programowaniu Typy Typy wyższych rzędów Ciekawy przykład Example type List type Function1 def id ( i : Int ) = i type Id [ A ] = A def apply ( f : Int = > Int , i : Int ) = f ( a ) type Apply [ A [ _ ] , B ] = A [ B ] Sztuczki w programowaniu Typy Typy wyższych rzędów Ciekawy przykład Example // List :: * = > * type List // Function1 :: (* x *) = > * type Function1 // id : ( Int ) = > Int def id ( i : Int ) = i // Id :: * = > * type Id [ A ] = A // apply : (( Int = > Int ) , Int ) = > Int def apply ( f : Int = > Int , i : Int ) = f ( a ) // Apply :: ((* = > *) x *) = > * type Apply [ A [ _ ] , B ] = A [ B ] Sztuczki w programowaniu Typy Typy wyższych rzędów Mnemonics Co dałoby się poprawić w sposobie w jaki generujemy dynamicznie bajtkod? Sztuczki w programowaniu Typy Typy wyższych rzędów Transformacje stosu 54 -12 R iadd 42 R Sztuczki w programowaniu Typy Typy wyższych rzędów Prosta transformacja stosu Example def appendC alField ( calField : Int ) ( start : F ): F = { // Convention : // - Local variable 1 is the Calendar object // - Before this block , StringBuilder on top of the stack // - After this block , StringBuilder on top of the stack start ~ aload (1) ~ bipush ( calField ) ~ method2 (( _ : Calendar ). get ( _ : Int )) ~ method2 (( _ : StringBuilder ). append ( _ : Int )) } trait F { def ~( f : F = > F ): F } Sztuczki w programowaniu Typy Typy wyższych rzędów Prosta transformacja stosu Example def appendC alField [ R <: Stack ] ( calField : Int ) ( start : F [ R ** StringBuilder ]): F [ R ** StringBuilder ] = { // Convention : // - Local variable 1 is the Calendar object // - Before this block , StringBuilder on top of the stack // - After this block , StringBuilder on top of the stack start ~ aload (1) ~ bipush ( calField ) ~ method2 (( _ : Calendar ). get ( _ : Int )) ~ method2 (( _ : StringBuilder ). append ( _ : Int )) } trait F [+ ST <: Stack ] { def ~[ ResST <: Stack ]( f : F [ Stack ] = > F [ ResST ]): F [ ResST ] } Sztuczki w programowaniu Typy Typy wyższych rzędów Implementacja Example trait Stack trait Nil extends Stack case class Cons [+ R <: Stack , + T ] extends Stack // Infix type alias type ** [ X <: Stack , Y ] = Cons [X , Y ] Sztuczki w programowaniu Typy Typy wyższych rzędów Więcej? HOList HOMap Obliczenia na poziomie typów ... Sztuczki w programowaniu Typy Typy wyższych rzędów Wnioski Typy wyższych rzędów Dają gwarancje poprawności tam, gdzie możnaby się ich nie spodziewać Bardziej generyczne otypowanie Sztuczki w programowaniu Typy Typy wyższych rzędów Klasy abstrakcji na typach Klasy abstrakcji typów Sztuczki w programowaniu Typy Typy wyższych rzędów Przykład Example sum ( List (1 , 2 , 3 , 4)) // = > 10 sum ( List (3.14 , 2.72)) // = > 5.86 sum ( List ( " a " , " bc " )) // ?! Sztuczki w programowaniu Typy Typy wyższych rzędów Przykład Example trait Num [ A ] { val zero : A def add ( x : A , y : A ): A } def sum [ A ]( nums : List [ A ])( tc : Num [ A ]) = nums . foldLeft ( tc . zero )( tc . add ) Sztuczki w programowaniu Typy Typy wyższych rzędów Przykład Example object IntNum extends Num [ Int ] { val zero = 0 def add ( x : Int , y : Int ) = x + y } object DoubleNum extends Num [ Double ] { val zero = 0 d def add ( x : Double , y : Double ) = x + y } Sztuczki w programowaniu Typy Typy wyższych rzędów Przykład Example sum ( List (1 , 2 , 3 , 4))( IntNum ) sum ( List (3.14 , 2.72))( DoubleNum ) Sztuczki w programowaniu Typy Typy wyższych rzędów Przykład Example trait Num [ A ] { val zero : A def add ( x : A , y : A ): A } def sum [ A ]( nums : List [ A ])( tc : Num [ A ]) = nums . foldLeft ( tc . zero )( tc . add ) Sztuczki w programowaniu Typy Typy wyższych rzędów Przykład Example trait Num [ A ] { val zero : A def add ( x : A , y : A ): A } def sum [ A ]( nums : List [ A ])( implicit tc : Num [ A ]) = nums . foldLeft ( tc . zero )( tc . add ) Sztuczki w programowaniu Typy Typy wyższych rzędów Przykład Example object IntNum extends Num [ Int ] { val zero = 0 def add ( x : Int , y : Int ) = x + y } object DoubleNum extends Num [ Double ] { val zero = 0 d def add ( x : Double , y : Double ) = x + y } Sztuczki w programowaniu Typy Typy wyższych rzędów Przykład Example implicit object IntNum extends Num [ Int ] { val zero = 0 def add ( x : Int , y : Int ) = x + y } implicit object DoubleNum extends Num [ Double ] { val zero = 0 d def add ( x : Double , y : Double ) = x + y } Sztuczki w programowaniu Typy Typy wyższych rzędów Przykład Example sum ( List (1 , 2 , 3 , 4))( IntNum ) sum ( List (3.14 , 2.72))( DoubleNum ) Sztuczki w programowaniu Typy Typy wyższych rzędów Przykład Example sum ( List (1 , 2 , 3 , 4)) sum ( List (3.14 , 2.72)) Sztuczki w programowaniu Typy Typy wyższych rzędów Przykład Example sum ( List (1 , 2 , 3 , 4)) sum ( List (3.14 , 2.72)) object MyIntNum extends Num [ Int ] { val zero = 1 val add ( x : Int , y : Int ) = x * y } sum ( List (1 , 2 , 3 , 4))( MyIntNum ) Sztuczki w programowaniu Typy Typy wyższych rzędów Inne przykłady Ord, Eq Coerce Collects, FiniteMap Addable Sztuczki w programowaniu Typy Typy wyższych rzędów Wnioski Klasy abstrakcji typów Fajniejszy sposób na pokazanie pewnych zależności między typami Pomaga w utrzymaniu Single Responsibility Principle Sztuczki w programowaniu Przepływ sterowania Kontynuacje Kontynuacje Właściwie ograniczone kontynuacje Sztuczki w programowaniu Przepływ sterowania Go To considered harmful Sztuczki w programowaniu Przepływ sterowania Głupie przykłady goto wyjątki AIO ... Sztuczki w programowaniu Przepływ sterowania Obsługa zdarzeń? 1 3 kodu aplikacji desktopowych Adobe stanowi obsługa zdarzeń 1 2 zgłaszanych błędów pochodzi z tego kodu Sztuczki w programowaniu Przepływ sterowania Obsługa zdarzeń? 1 3 kodu aplikacji desktopowych Adobe stanowi obsługa zdarzeń 1 2 zgłaszanych błędów pochodzi z tego kodu dlaczego mają tam tyle błędów? Sztuczki w programowaniu Przepływ sterowania Inwersja kontroli Example var path : Path = null val moveObserver = { ( event : MouseEvent ) = > path . lineTo ( event . position ) draw ( path ) } control . a d d M o u s e D o w n O b s e r v e r { event = > path = new Path ( event . position ) control . a d d M o u s e M o v e O b s e r v e r ( moveObserver ) } control . a d d M o u s e U p O b s e r v e r { event = > control . r e m o v e M o u s e M o v e O b s e r v e r ( moveObserver ) path . close () draw ( path ) } Sztuczki w programowaniu Przepływ sterowania Co jest złego w tym kodzie? Efekty uboczne ¬ Enkapsulacja Brak kompozycyjności Niski poziom abstrakcji Sztuczki w programowaniu Przepływ sterowania Wyższa abstrakcja? Example var path : Path = null var moveObserver = null observe ( control . mouseDown ) { event = > path = new Path ( event . position ) moveObserver = observe ( control . mouseMoves ) { event = > path . lineTo ( event . position ) draw ( path ) } } observe ( control . mouseUp ) { event = > moveObserver . dispose () path . close () draw ( path ) } Sztuczki w programowaniu Przepływ sterowania Bez inwersji kontroli? Example Reactor . once { self = > // step 1: val path = new Path (( self next mouseDown ). position ) // step 2: self loopUntil mouseUp { val m = self next mouseMove path . lineTo ( m . position ) draw ( path ) } // step 3: path . close () draw ( path ) } Sztuczki w programowaniu Przepływ sterowania Signal Whitening Example val path : Signal [ Path ] = Val ( new Path ) once { self = > import self . _ val down = next ( mouseDown ) emit ( previous . moveTo ( down . position )) loopUntil ( mouseUp ) { val m = next ( mouseMove ) emit ( previous . lineTo ( m . position )) } emit ( previous . close ) } Sztuczki w programowaniu Przepływ sterowania Wnioski Kontynuacje umożliwiają enkapsulację inwersji kontroli