diff options
author | msimionato <msimionato@msimionato.uxmi.statnet.statpro.com> | 2010-05-10 17:27:53 +0200 |
---|---|---|
committer | msimionato <msimionato@msimionato.uxmi.statnet.statpro.com> | 2010-05-10 17:27:53 +0200 |
commit | bcfdacd57f45f049f9f54d7074389d0bf8e596f7 (patch) | |
tree | 59b8e9e3bd65381ed47964cf0e80717702957484 /pypers | |
parent | b055201ebb7686783c0be59078a7bd540715c785 (diff) | |
download | micheles-bcfdacd57f45f049f9f54d7074389d0bf8e596f7.tar.gz |
Added my talks at PyConIt 2010
Diffstat (limited to 'pypers')
-rw-r--r-- | pypers/pycon10/abstract.txt | 29 | ||||
-rw-r--r-- | pypers/pycon10/cloc-trunk.txt | 42 | ||||
-rw-r--r-- | pypers/pycon10/for_rec.py | 16 | ||||
-rw-r--r-- | pypers/pycon10/menu1.py | 13 | ||||
-rw-r--r-- | pypers/pycon10/menu2.py | 13 | ||||
-rw-r--r-- | pypers/pycon10/recruiting.txt | 145 | ||||
-rw-r--r-- | pypers/pycon10/statpro-home-page.png | bin | 0 -> 248968 bytes | |||
-rw-r--r-- | pypers/pycon10/stream-ex.ss | 7 | ||||
-rw-r--r-- | pypers/pycon10/talk.html | 824 | ||||
-rw-r--r-- | pypers/pycon10/talk.txt | 487 | ||||
-rw-r--r-- | pypers/pycon10/tco.py | 4 | ||||
-rw-r--r-- | pypers/pycon10/tco.ss | 4 |
12 files changed, 1584 insertions, 0 deletions
diff --git a/pypers/pycon10/abstract.txt b/pypers/pycon10/abstract.txt new file mode 100644 index 0000000..84c6335 --- /dev/null +++ b/pypers/pycon10/abstract.txt @@ -0,0 +1,29 @@ +;; This buffer is for notes you don't want to save, and for Lisp evaluation. +;; If you want to create a file, visit that file with C-x C-f, +;; then enter the text in that file's own buffer. + +Introduzione alla programmazione funzionale +--------------------------------------------------------- + +In questo talk interattivo io e Nicola Larosa cercheremo di illustrare +cosa significa "pensare in maniera funzionale" quando si lavora +in un linguaggio come Python che funzionale non e'. + +In primo luogo cercheremo di sfatare alcuni luoghi comuni sulla +programmazione funzionale (programmare funzionalmente NON vuol +dire usare map, filter e reduce). In secondo luogo mostreremo +vari errori comuni che non si commetterebbero se si pensasse in +maniera piu' funzionale (mutare variabili globali, basarsi su effetti +collaterali, eccetera). La terza parte del talk sara' un po' piu' +esplorativa, mostrando qualche scorcio del vasto mondo funzionale +che esiste al di fuori di Python: in particolare mostreremo le +differenze tra un linguaggio con qualche supporto per la +programmazione funzionale (come Python) ed un "vero" linguaggio funzionale. + +Lo scopo del talk e' quello di illustrare tecniche che permettono +di scrivere codice piu' semplice e piu' manutenibile, non quello +di decantare le virtu' della programmazione funzionale ad ogni +costo. Non ci interessa spiegare come convertire tutti i for loop +in chiamate ricorsive: l'interessante e' discutere come +utilizzare un design funzionale piuttosto che uno basato su +mutazione ed effetti collaterali. diff --git a/pypers/pycon10/cloc-trunk.txt b/pypers/pycon10/cloc-trunk.txt new file mode 100644 index 0000000..50e22ab --- /dev/null +++ b/pypers/pycon10/cloc-trunk.txt @@ -0,0 +1,42 @@ +~$ cloc trunk + 11719 text files. + 11356 unique files. + 41343 files ignored. + +http://cloc.sourceforge.net v 1.08 T=125.0 s (73.9 files/s, 17064.8 lines/s) +-------------------------------------------------------------------------------- +Language files blank comment code scale 3rd gen. equiv +-------------------------------------------------------------------------------- +C++ 1694 77757 53011 340336 x 1.51 = 513907.36 +XML 574 2940 2554 249269 x 1.90 = 473611.10 +ASP 788 45807 4748 245940 x 1.29 = 317262.60 +Python 1986 38493 44093 176747 x 4.20 = 742337.40 +C/C++ Header 2349 57741 78168 160979 x 1.00 = 160979.00 +Javascript 173 26941 10581 121496 x 1.48 = 179814.08 +SQL 452 20768 4852 104580 x 2.29 = 239488.20 +C 334 14465 12988 99273 x 0.77 = 76440.21 +C# 104 4454 2502 27524 x 1.36 = 37432.64 +CSS 116 1618 1087 23786 x 1.00 = 23786.00 +Visual Basic 33 4130 5800 14136 x 2.76 = 39015.36 +make 269 1714 752 8978 x 2.50 = 22445.00 +Bourne Shell 51 598 1284 6556 x 3.81 = 24978.36 +m4 27 591 627 5552 x 1.00 = 5552.00 +MSBuild scripts 9 2 7 4878 x 1.90 = 9268.20 +IDL 23 592 0 3784 x 3.80 = 14379.20 +HTML 32 330 76 3767 x 1.90 = 7157.30 +Lisp 33 561 648 2702 x 1.25 = 3377.50 +DOS Batch 83 915 463 1159 x 0.63 = 730.17 +Ruby 13 272 97 1141 x 4.20 = 4792.20 +Perl 6 104 131 922 x 4.00 = 3688.00 +Java 3 131 173 534 x 1.36 = 726.24 +XSD 6 0 0 506 x 1.90 = 961.40 +awk 5 65 17 366 x 3.81 = 1394.46 +DTD 4 117 50 351 x 1.90 = 666.90 +ASP.Net 45 186 714 330 x 1.29 = 425.70 +Bourne Again Shell 12 63 8 245 x 3.81 = 933.45 +XSLT 1 15 14 196 x 1.90 = 372.40 +NAnt scripts 3 27 0 119 x 1.90 = 226.10 +Teamcenter def 10 16 0 93 x 1.00 = 93.00 +-------------------------------------------------------------------------------- +SUM: 9238 301413 225445 1606245 x 1.81 = 2906241.53 +-------------------------------------------------------------------------------- diff --git a/pypers/pycon10/for_rec.py b/pypers/pycon10/for_rec.py new file mode 100644 index 0000000..166a67d --- /dev/null +++ b/pypers/pycon10/for_rec.py @@ -0,0 +1,16 @@ +def loop123(): + for i in 1, 2, 3: + # ..... + print i + # ..... + +def loop123(i=1): + if i > 3: + return + else: + # ..... + print i + # ..... + loop123(i+1) + +loop123() diff --git a/pypers/pycon10/menu1.py b/pypers/pycon10/menu1.py new file mode 100644 index 0000000..3259c32 --- /dev/null +++ b/pypers/pycon10/menu1.py @@ -0,0 +1,13 @@ +import Tkinter +root = Tkinter.Tk() +text = Tkinter.StringVar() +label = Tkinter.Label(root, textvariable=text) +menubar = Tkinter.Menu(root) +menu = Tkinter.Menu(menubar) +menubar.add_cascade(label='File', menu=menu) +for item in ['F1','F2', 'F3']: + def showmenu(): + text.set(text.get() + '\n%s' % item) + menu.add_command(label=item, command=showmenu) +root.config(menu=menubar); label.pack() +root.mainloop() diff --git a/pypers/pycon10/menu2.py b/pypers/pycon10/menu2.py new file mode 100644 index 0000000..d148389 --- /dev/null +++ b/pypers/pycon10/menu2.py @@ -0,0 +1,13 @@ +import Tkinter +root = Tkinter.Tk() +text = Tkinter.StringVar() +label = Tkinter.Label(root, textvariable=text) +menubar = Tkinter.Menu(root) +menu = Tkinter.Menu(menubar) +menubar.add_cascade(label='File', menu=menu) +for item in ['F1','F2', 'F3']: + def showmenu(item=item): + text.set(text.get() + '\n%s' % item) + menu.add_command(label=item, command=showmenu) +root.config(menu=menubar); label.pack() +root.mainloop() diff --git a/pypers/pycon10/recruiting.txt b/pypers/pycon10/recruiting.txt new file mode 100644 index 0000000..694bc20 --- /dev/null +++ b/pypers/pycon10/recruiting.txt @@ -0,0 +1,145 @@ +StatPro +---------------------------------- + +.. image:: statpro-home-page.png + +Cloc +-------------------------- + +:: + + ~$ cloc trunk + 11719 text files. + 11356 unique files. + 41343 files ignored. + + http://cloc.sourceforge.net v 1.08 T=125.0 s (73.9 files/s, 17064.8 lines/s) + -------------------------------------------------------------------------------- + Language files blank comment code scale 3rd gen. equiv + -------------------------------------------------------------------------------- + C++ 1694 77757 53011 340336 x 1.51 = 513907.36 + XML 574 2940 2554 249269 x 1.90 = 473611.10 + ASP 788 45807 4748 245940 x 1.29 = 317262.60 + Python 1986 38493 44093 176747 x 4.20 = 742337.40 + C/C++ Header 2349 57741 78168 160979 x 1.00 = 160979.00 + Javascript 173 26941 10581 121496 x 1.48 = 179814.08 + SQL 452 20768 4852 104580 x 2.29 = 239488.20 + C 334 14465 12988 99273 x 0.77 = 76440.21 + C# 104 4454 2502 27524 x 1.36 = 37432.64 + CSS 116 1618 1087 23786 x 1.00 = 23786.00 + Visual Basic 33 4130 5800 14136 x 2.76 = 39015.36 + make 269 1714 752 8978 x 2.50 = 22445.00 + Bourne Shell 51 598 1284 6556 x 3.81 = 24978.36 + m4 27 591 627 5552 x 1.00 = 5552.00 + MSBuild scripts 9 2 7 4878 x 1.90 = 9268.20 + IDL 23 592 0 3784 x 3.80 = 14379.20 + HTML 32 330 76 3767 x 1.90 = 7157.30 + Lisp 33 561 648 2702 x 1.25 = 3377.50 + DOS Batch 83 915 463 1159 x 0.63 = 730.17 + Ruby 13 272 97 1141 x 4.20 = 4792.20 + Perl 6 104 131 922 x 4.00 = 3688.00 + Java 3 131 173 534 x 1.36 = 726.24 + XSD 6 0 0 506 x 1.90 = 961.40 + awk 5 65 17 366 x 3.81 = 1394.46 + DTD 4 117 50 351 x 1.90 = 666.90 + ASP.Net 45 186 714 330 x 1.29 = 425.70 + Bourne Again Shell 12 63 8 245 x 3.81 = 933.45 + XSLT 1 15 14 196 x 1.90 = 372.40 + NAnt scripts 3 27 0 119 x 1.90 = 226.10 + Teamcenter def 10 16 0 93 x 1.00 = 93.00 + -------------------------------------------------------------------------------- + SUM: 9238 301413 225445 1606245 x 1.81 = 2906241.53 + -------------------------------------------------------------------------------- + +Tecnologie che usiamo +--------------------- + +*troppe!* + +.. class:: incremental + + + Python (2.5) + + SQLAlchemy + + Pylons + + Mako + + Zope/Plone + + Twisted + +... lato python +--------------- + +.. class:: incremental + + + ipython + + virtualenv + + easy_install + + nosetests + + pylint/pyflakes + + matplotlib + + tutti quelli che ha menzionato Raymond e non solo + +Non di solo Python ... +----------------------- + + + C++ + + swig + + + ExtJS + + JQuery + + Flash + + + XML + + SQLite + +Lato Linux +---------- + + + PostgreSQL + + Grid engine + + sfs + + emacs + + vi + + ... + +Lato Windows +------------ + + + SQLServer + + CORBA + + COM + + VB6/ASP + + VC++ + + ... + +Organigramma di Milano +------------------------------------- + +.. class:: incremental + ++ Quants 3 (+1) ++ Techs 5 (+1) ++ BCD 2 ++ SysAdmin 1 ++ Customer support 1 ++ Data Management Team 6 ++ venditori, manager, segretaria, etc + +Requisiti +------------------------- + +- sul sito c'e' la descrizione ufficiale della figura professionale ricercata: + ignoratela! + +.. class:: incremental + +- basta che sia bravo! + +- non si fanno distinzioni di genere, razza o nazionalita' + +Non siate timiti +----------------------------- + +- se avete collaborato/collaborate a progetti open source, ditelo +- se siete esperti di qualunque linguaggio esotico, ditelo +- mandate i curriculum a luca.fittabile@statpro.com +- se siete esperti di finanza invece a marco.marchioro@statpro.com diff --git a/pypers/pycon10/statpro-home-page.png b/pypers/pycon10/statpro-home-page.png Binary files differnew file mode 100644 index 0000000..02f8e7d --- /dev/null +++ b/pypers/pycon10/statpro-home-page.png diff --git a/pypers/pycon10/stream-ex.ss b/pypers/pycon10/stream-ex.ss new file mode 100644 index 0000000..4ae56c3 --- /dev/null +++ b/pypers/pycon10/stream-ex.ss @@ -0,0 +1,7 @@ +(import (rnrs) (srfi :41 streams)) + +(define str123 (stream-range 1 4)) + +(stream-for-each display str123) + +(stream-for-each display str123) diff --git a/pypers/pycon10/talk.html b/pypers/pycon10/talk.html new file mode 100644 index 0000000..44f1c21 --- /dev/null +++ b/pypers/pycon10/talk.html @@ -0,0 +1,824 @@ +<?xml version="1.0" encoding="utf-8" ?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> +<meta name="generator" content="Docutils 0.5: http://docutils.sourceforge.net/" /> +<meta name="version" content="S5 1.1" /> +<title>Introduction to Functional Programming</title> +<meta name="date" content="2010-05-08" /> +<style type="text/css"> + +/* +:Author: David Goodger (goodger@python.org) +:Id: $Id: html4css1.css 5196 2007-06-03 20:25:28Z wiemann $ +:Copyright: This stylesheet has been placed in the public domain. + +Default cascading style sheet for the HTML output of Docutils. + +See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to +customize this style sheet. +*/ + +/* used to remove borders from tables and images */ +.borderless, table.borderless td, table.borderless th { + border: 0 } + +table.borderless td, table.borderless th { + /* Override padding for "table.docutils td" with "! important". + The right padding separates the table cells. */ + padding: 0 0.5em 0 0 ! important } + +.first { + /* Override more specific margin styles with "! important". */ + margin-top: 0 ! important } + +.last, .with-subtitle { + margin-bottom: 0 ! important } + +.hidden { + display: none } + +a.toc-backref { + text-decoration: none ; + color: black } + +blockquote.epigraph { + margin: 2em 5em ; } + +dl.docutils dd { + margin-bottom: 0.5em } + +/* Uncomment (and remove this text!) to get bold-faced definition list terms +dl.docutils dt { + font-weight: bold } +*/ + +div.abstract { + margin: 2em 5em } + +div.abstract p.topic-title { + font-weight: bold ; + text-align: center } + +div.admonition, div.attention, div.caution, div.danger, div.error, +div.hint, div.important, div.note, div.tip, div.warning { + margin: 2em ; + border: medium outset ; + padding: 1em } + +div.admonition p.admonition-title, div.hint p.admonition-title, +div.important p.admonition-title, div.note p.admonition-title, +div.tip p.admonition-title { + font-weight: bold ; + font-family: sans-serif } + +div.attention p.admonition-title, div.caution p.admonition-title, +div.danger p.admonition-title, div.error p.admonition-title, +div.warning p.admonition-title { + color: red ; + font-weight: bold ; + font-family: sans-serif } + +/* Uncomment (and remove this text!) to get reduced vertical space in + compound paragraphs. +div.compound .compound-first, div.compound .compound-middle { + margin-bottom: 0.5em } + +div.compound .compound-last, div.compound .compound-middle { + margin-top: 0.5em } +*/ + +div.dedication { + margin: 2em 5em ; + text-align: center ; + font-style: italic } + +div.dedication p.topic-title { + font-weight: bold ; + font-style: normal } + +div.figure { + margin-left: 2em ; + margin-right: 2em } + +div.footer, div.header { + clear: both; + font-size: smaller } + +div.line-block { + display: block ; + margin-top: 1em ; + margin-bottom: 1em } + +div.line-block div.line-block { + margin-top: 0 ; + margin-bottom: 0 ; + margin-left: 1.5em } + +div.sidebar { + margin: 0 0 0.5em 1em ; + border: medium outset ; + padding: 1em ; + background-color: #ffffee ; + width: 40% ; + float: right ; + clear: right } + +div.sidebar p.rubric { + font-family: sans-serif ; + font-size: medium } + +div.system-messages { + margin: 5em } + +div.system-messages h1 { + color: red } + +div.system-message { + border: medium outset ; + padding: 1em } + +div.system-message p.system-message-title { + color: red ; + font-weight: bold } + +div.topic { + margin: 2em } + +h1.section-subtitle, h2.section-subtitle, h3.section-subtitle, +h4.section-subtitle, h5.section-subtitle, h6.section-subtitle { + margin-top: 0.4em } + +h1.title { + text-align: center } + +h2.subtitle { + text-align: center } + +hr.docutils { + width: 75% } + +img.align-left { + clear: left } + +img.align-right { + clear: right } + +ol.simple, ul.simple { + margin-bottom: 1em } + +ol.arabic { + list-style: decimal } + +ol.loweralpha { + list-style: lower-alpha } + +ol.upperalpha { + list-style: upper-alpha } + +ol.lowerroman { + list-style: lower-roman } + +ol.upperroman { + list-style: upper-roman } + +p.attribution { + text-align: right ; + margin-left: 50% } + +p.caption { + font-style: italic } + +p.credits { + font-style: italic ; + font-size: smaller } + +p.label { + white-space: nowrap } + +p.rubric { + font-weight: bold ; + font-size: larger ; + color: maroon ; + text-align: center } + +p.sidebar-title { + font-family: sans-serif ; + font-weight: bold ; + font-size: larger } + +p.sidebar-subtitle { + font-family: sans-serif ; + font-weight: bold } + +p.topic-title { + font-weight: bold } + +pre.address { + margin-bottom: 0 ; + margin-top: 0 ; + font-family: serif ; + font-size: 100% } + +pre.literal-block, pre.doctest-block { + margin-left: 2em ; + margin-right: 2em } + +span.classifier { + font-family: sans-serif ; + font-style: oblique } + +span.classifier-delimiter { + font-family: sans-serif ; + font-weight: bold } + +span.interpreted { + font-family: sans-serif } + +span.option { + white-space: nowrap } + +span.pre { + white-space: pre } + +span.problematic { + color: red } + +span.section-subtitle { + /* font-size relative to parent (h1..h6 element) */ + font-size: 80% } + +table.citation { + border-left: solid 1px gray; + margin-left: 1px } + +table.docinfo { + margin: 2em 4em } + +table.docutils { + margin-top: 0.5em ; + margin-bottom: 0.5em } + +table.footnote { + border-left: solid 1px black; + margin-left: 1px } + +table.docutils td, table.docutils th, +table.docinfo td, table.docinfo th { + padding-left: 0.5em ; + padding-right: 0.5em ; + vertical-align: top } + +table.docutils th.field-name, table.docinfo th.docinfo-name { + font-weight: bold ; + text-align: left ; + white-space: nowrap ; + padding-left: 0 } + +h1 tt.docutils, h2 tt.docutils, h3 tt.docutils, +h4 tt.docutils, h5 tt.docutils, h6 tt.docutils { + font-size: 100% } + +ul.auto-toc { + list-style-type: none } + +</style> +<!-- configuration parameters --> +<meta name="defaultView" content="slideshow" /> +<meta name="controlVis" content="hidden" /> +<!-- style sheet links --> +<script src="ui/default/slides.js" type="text/javascript"></script> +<link rel="stylesheet" href="ui/default/slides.css" + type="text/css" media="projection" id="slideProj" /> +<link rel="stylesheet" href="ui/default/outline.css" + type="text/css" media="screen" id="outlineStyle" /> +<link rel="stylesheet" href="ui/default/print.css" + type="text/css" media="print" id="slidePrint" /> +<link rel="stylesheet" href="ui/default/opera.css" + type="text/css" media="projection" id="operaFix" /> + +<style type="text/css"> +#currentSlide {display: none;} +</style> +</head> +<body> +<div class="layout"> +<div id="controls"></div> +<div id="currentSlide"></div> +<div id="header"> + +</div> +<div id="footer"> +<h1>Introduction to Functional Programming</h1> +<h2>PyCon Italia 2010</h2> +</div> +</div> +<div class="presentation"> +<div class="slide" id="slide0"> +<h1 class="title">Introduction to Functional Programming</h1> +<table class="docinfo" frame="void" rules="none"> +<col class="docinfo-name" /> +<col class="docinfo-content" /> +<tbody valign="top"> +<tr class="field"><th class="docinfo-name">Event:</th><td class="field-body">PyCon Italia 2010</td> +</tr> +<tr class="field"><th class="docinfo-name">Presenters:</th><td class="field-body">Michele Simionato, Nicola Larosa</td> +</tr> +<tr><th class="docinfo-name">Date:</th> +<td>2010-05-08</td></tr> +</tbody> +</table> +<!-- Definitions of interpreted text roles (classes) for S5/HTML data. --> +<!-- This data file has been placed in the public domain. --> +<!-- Colours +======= --> +<!-- Text Sizes +========== --> +<!-- Display in Slides (Presentation Mode) Only +========================================== --> +<!-- Display in Outline Mode Only +============================ --> +<!-- Display in Print Only +===================== --> +<!-- Display in Handout Mode Only +============================ --> +<!-- Incremental Display +=================== --> + +</div> +<div class="slide" id="my-goals-for-this-talk"> +<h1>My goals for this talk</h1> +<ul class="simple"> +<li>this is an introductory talk (no monads, no combinators, +no pattern matching, no concurrency, no blurb)</li> +<li>mostly about Python, with 1 or 2 examples in Scheme</li> +<li>a first part for purists (rebinding and TCO)</li> +<li>a second part focused on functional <em>design</em> in real life</li> +<li>also a few remarks about functional aspects in automatic testing +and database programming</li> +</ul> +</div> +<div class="slide" id="first-two-misconceptions"> +<h1>First: two misconceptions</h1> +<ul class="simple"> +<li>two extreme viewpoints:</li> +</ul> +<blockquote> +<ol class="arabic simple"> +<li>FP means map, filter, reduce</li> +<li>FP means Haskell</li> +</ol> +</blockquote> +<ul class="incremental simple"> +<li>I am in between</li> +<li>readability and maintainability are my first concerns, not purity</li> +<li>... still purity is attractive</li> +</ul> +</div> +<div class="slide" id="what-functional-means"> +<h1>What functional means</h1> +<ul> +<li><p class="first">a lot of emphasis on the functions in the past, nowadays people stress +more the data structures</p> +</li> +<li><p class="first">a language is functional if</p> +<blockquote> +<ol class="incremental arabic simple"> +<li><em>variables cannot be re-assigned</em></li> +<li><em>data structures cannot be modified</em></li> +<li><em>I/O side effects are confined</em></li> +</ol> +</blockquote> +</li> +</ul> +</div> +<div class="slide" id="less-is-more"> +<h1>Less is more</h1> +<ul class="simple"> +<li>FP is about having <em>less</em> features, not more!</li> +</ul> +<ul class="incremental simple"> +<li>less ways of shooting yourself in the foot</li> +<li>the functional programmer is often compared to a monk</li> +<li>the challenge: can we program under such strong constraints??</li> +</ul> +</div> +<div class="slide" id="point-1-immutable-bindings"> +<h1>Point 1: immutable bindings</h1> +<ul class="simple"> +<li>there are no more variables as we intend them</li> +<li>what about for loops?</li> +</ul> +<pre class="doctest-block"> +>>> for item in sequence: + do_something(item) +</pre> +<ul class="incremental simple"> +<li>the name <tt class="docutils literal"><span class="pre">item</span></tt> is rebound at each iteration</li> +<li>(talk about boxes and labels)</li> +<li>for loops have worked well for 50+ years, it seems impossible to +live without them</li> +<li>... but do for loops work really well?</li> +</ul> +</div> +<div class="slide" id="let-s-see"> +<h1>Let's see ...</h1> +<pre class="literal-block"> +root = Tkinter.Tk() +text = Tkinter.StringVar() +label = Tkinter.Label(root, textvariable=text) +menubar = Tkinter.Menu(root) +menu = Tkinter.Menu(menubar) +menubar.add_cascade(label='File', menu=menu) +for item in ['F1','F2', 'F3']: + def showmenu(): + text.set(text.get() + '\n%s' % item) + menu.add_command(label=item, command=showmenu) +root.config(menu=menubar); label.pack() +root.mainloop() +</pre> +</div> +<div class="slide" id="the-usual-hack"> +<h1>The usual hack</h1> +<pre class="literal-block"> +root = Tkinter.Tk() +text = Tkinter.StringVar() +label = Tkinter.Label(root, textvariable=text) +menubar = Tkinter.Menu(root) +menu = Tkinter.Menu(menubar) +menubar.add_cascade(label='File', menu=menu) +for item in ['F1','F2', 'F3']: + def showmenu(item=item): + text.set(text.get() + '\n%s' % item) + menu.add_command(label=item, command=showmenu) +root.config(menu=menubar); label.pack() +root.mainloop() +</pre> +</div> +<div class="slide" id="this-is-still-a-hack"> +<h1>This is still a hack</h1> +<ul class="simple"> +<li>looking at the interaction of for-loops and closures is a good +test to see if a language is functional</li> +</ul> +<ul class="incremental simple"> +<li>this is an example of the difference between a truly functional language +and an imperative language with some support for FP</li> +<li>Python, Common Lisp, Go do <em>not</em> pass the test</li> +<li>Haskell, Scheme, Clojure, Perl <em>do</em> pass the test</li> +</ul> +</div> +<div class="slide" id="a-simpler-example"> +<h1>A simpler example</h1> +<pre class="literal-block"> +lst = [] +for i in 0, 1, 2: + def f(): + print i + lst.append(f) + +lst[0]() #=> 2 +lst[1]() #=> 2 +lst[2]() #=> 2 +</pre> +</div> +<div class="slide" id="a-partial-solution"> +<h1>A partial solution</h1> +<pre class="literal-block"> +lst = [] +def append_f(i): + def f(): + print i + lst.append(f) + +for i in 0, 1, 2: # mutation is still there + append_f(i) + +lst[0]() #=> 0 +lst[1]() #=> 1 +lst[2]() #=> 2 +</pre> +</div> +<div class="slide" id="the-question"> +<h1>The question</h1> +<p>We need a way to compute a loop <em>without</em> rebinding the loop variable:</p> +<pre class="literal-block"> +def loop123(): + for i in 1, 2, 3: + <do_something(i)> + +loop123() +</pre> +<p>Clearly we need to introduce a new scope at each iteration</p> +</div> +<div class="slide" id="the-answer-recursion"> +<h1>The answer: recursion</h1> +<p>Convert the function containing a loop in a recursive function with an +additional argument (the loop index):</p> +<pre class="literal-block"> +def loop123(i=1): + if i > 3: + return + else: + <do_something(i)> + loop123(i+1) # tail call + +loop123() +</pre> +</div> +<div class="slide" id="another-example-enumerate"> +<h1>Another example: enumerate</h1> +<p>Here is an example of a for loop used to build a data structure:</p> +<pre class="literal-block"> +def enumerate(values, i=0, acc=()): + if i >= len(values): + return acc + else: + return enumerate( + values, i+1, acc + ((i, values[i]),)) +</pre> +<p>The trick is to use an (immutable) accumulator</p> +</div> +<div class="slide" id="the-importance-of-tail-calls"> +<h1>The importance of tail calls</h1> +<ul class="simple"> +<li>in a truly functional language all for-loops are implemented as +recursive functions in tail call form</li> +</ul> +<ul class="incremental simple"> +<li>the compiler is able to recognize them and avoids creating +a new stack per each iteration (TCO): no recursion limit</li> +<li>for the programmer POV the loop variable is never reassigned</li> +<li>it seems <em>perverse</em> but it is essential</li> +<li>expecially for things like pattern matching</li> +</ul> +</div> +<div class="slide" id="a-note-on-pattern-matching"> +<h1>A note on pattern matching</h1> +<p>Pattern matching is used a lot in functional languages; in Scheme is +especially used in macro programming; in ML and Haskell is used everywhere, +even to define functions:</p> +<pre class="literal-block"> +fun fact n = fact' (n, 1) +and fact' (0, acc) = acc + | fact' (n, acc) = fact' (n-1, acc*n); +</pre> +<p>In Erlang is used to destructure the messages sent to the Erlang lightweight +processes.</p> +</div> +<div class="slide" id="tco-and-debugging"> +<h1>TCO and debugging</h1> +<p>There has been some heated debate last year due to Guido's dismissal +of TCO.</p> +<p class="incremental">In all fairness I must notice that TCO does not prevent debugging:</p> +<pre class="incremental literal-block"> +;; tco.ss +(let loop ((x -3)) + (/ 1 x) + (loop (add1 x))) +</pre> +<p class="incremental">(but I agree that iterative is better than recursive)</p> +</div> +<div class="slide" id="the-pragmatist-voice"> +<h1>The pragmatist voice</h1> +<p><em>Python programs written in functional style usually won’t go to the +extreme of avoiding all I/O or all assignments; instead, they’ll +provide a functional-appearing interface but will use non-functional +features internally. For example, the implementation of a function +will still use assignments to local variables, but won’t modify global +variables or have other side effects.</em> -- Andrew Kuchling</p> +</div> +<div class="slide" id="example-collecting-objects"> +<h1>Example: collecting objects</h1> +<pre class="literal-block"> +def collect_mail(mails): + storage = {} + for mail in mails: + client = mail.getclient() + kind = mail.getkind() + date = mail.getdate() + stored = storage.get((client, kind)) + if stored is None or stored.date < date: + storage[client, kind] = mail + return storage +</pre> +</div> +<div class="slide" id="for-purists-at-all-costs"> +<h1>For purists at all costs ...</h1> +<ul> +<li><p class="first">you can avoid mutation of the storage by introducting a functional +update utility:</p> +<pre class="literal-block"> +def update(d, k, v): + newd = d.copy() + newd.update({k : v}) + return newd +</pre> +</li> +</ul> +</div> +<div class="slide" id="use-a-helper-function"> +<h1>... use a helper function</h1> +<pre class="literal-block"> +def _collect_mail(storage, mail): + # a left-folding function: acc,obj->new-acc + client = mail.getclient() + kind = mail.getkind() + date = mail.getdate() + stored = storage.get((client, kind)) + if stored is None or stored.date < date: + return update( + storage, (client, kind), mail) + else: + return storage +</pre> +</div> +<div class="slide" id="then-reduce-is-your-friend"> +<h1>... then reduce is your friend</h1> +<pre class="literal-block"> +def collect_mail(mails): + return reduce(_collect_mail, mails, {}) +</pre> +<ul class="incremental simple"> +<li>except that you should not be <em>perverse</em></li> +<li>there is no reason to be functional at any cost if you are using a +non-functional language</li> +<li>do not fight the language, flow with it</li> +<li>and this close the discussion about immutable bindings</li> +</ul> +</div> +<div class="slide" id="point-2-immutable-data"> +<h1>Point 2: immutable data</h1> +<ul class="simple"> +<li>we cannot really get rid of all mutable objects but +certainly mutable objects are overused</li> +</ul> +<ul class="incremental simple"> +<li>mutable objects are often evil +(globals, example of nosetests changing my sys.path)</li> +<li>mutable objects can be often avoided +(functional update may substitute mutation)</li> +<li>there are smart functional data structures nowadays +(not much in Python)</li> +<li>Python has only strings, (named)tuples and frozensets</li> +</ul> +</div> +<div class="slide" id="namedtuple"> +<h1>namedtuple</h1> +<pre class="doctest-block"> +>>> from collections import namedtuple +>>> Point = namedtuple('Point', 'x y') +>>> p1 = Point(0, 0) +>>> p2 = p1._replace(x=1) +>>> p2 +(1, 0) +>>> p1 is p2 +False +</pre> +<p>(requires copying the full structure in Python). Python also lacks +immutable dictionaries (say Red/Black trees).</p> +</div> +<div class="slide" id="generators-are-mutable"> +<h1>Generators are mutable</h1> +<p>Many Python programs (especially the ones using list/generator +comprehension and the itertools module) are considered in functional +style even if they are not functional from a purist POV:</p> +<blockquote> +<pre class="doctest-block"> +>>> it123 = iter([1, 2, 3]) +>>> for i in it123: print i, +... +1 2 3 +>>> for i in it123: print i, +... +</pre> +</blockquote> +</div> +<div class="slide" id="the-functional-way-streams"> +<h1>The functional way: streams</h1> +<p>Looping over a stream does not mutate it:</p> +<pre class="literal-block"> +> (import (srfi :41 streams)) +> (define str123 (stream-range 1 4)) +> (stream-for-each display str123) +123 +> (stream-for-each display str123) +123 +</pre> +<p>Yet another difference between Python and a true functional language</p> +</div> +<div class="slide" id="confined-side-effects"> +<h1>3: confined side effects?</h1> +<ul> +<li><p class="first">to be able to distinguish pure functions from +impure functions is important:</p> +<pre class="literal-block"> +@memoize +def do_something(): + result = long_computation() + log.info('Computed %s', result) + return result +</pre> +</li> +<li><p class="first">Haskell is the purest language when it comes to +confining side effects</p> +</li> +</ul> +</div> +<div class="slide" id="side-effects-and-unittests"> +<h1>Side effects and unittests</h1> +<ul class="simple"> +<li>in Python keeping the distinction between pure and impure functions is a +question of good coding style</li> +</ul> +<ul class="incremental simple"> +<li>common sense tells you that you should decouple I/O from the +business logic</li> +<li>unit testing is arguably the master path to functional design: +if you want to test a thing, make it a pure function</li> +<li>if it is difficult to test it is not functional</li> +</ul> +</div> +<div class="slide" id="usual-recommendations"> +<h1>Usual recommendations</h1> +<p>All the usual recommendations to make code more testable, such as</p> +<ul class="incremental simple"> +<li>avoid side effects (including the ones at import time)</li> +<li>do not use special variables (damned them!)</li> +<li>decouple the system i.e. pass objects around</li> +<li>write libraries, not frameworks</li> +<li>feel guilty!</li> +</ul> +<p>are recommendations to make code more functional.</p> +</div> +<div class="slide" id="database-connections-bad"> +<h1>Database connections (bad)</h1> +<p>db = DBSession() # singleton</p> +<dl class="docutils"> +<dt>def read(...):</dt> +<dd>db.read_data( ... )</dd> +<dt>def write(data, ...):</dt> +<dd>db.write_data(data, ...)</dd> +<dt>if __name__ == '__main__':</dt> +<dd>db.init(dsn)</dd> +</dl> +</div> +<div class="slide" id="database-connections-good"> +<h1>Database connections (good)</h1> +<dl class="docutils"> +<dt>def read(db, ...):</dt> +<dd>db.read_data( ... )</dd> +<dt>def write(db, data, ...):</dt> +<dd>db.write_data(data, ...)</dd> +<dt>if __name__ == '__main__':</dt> +<dd>db = DBSession(dsn)</dd> +</dl> +</div> +<div class="slide" id="fp-and-databases"> +<h1>FP and databases</h1> +<ul class="simple"> +<li>operating on a DB looks like the least functional thing you can do +(ex. <tt class="docutils literal"><span class="pre">importer.import(fileobj,</span> <span class="pre">db)</span></tt>)</li> +</ul> +<ul class="incremental"> +<li><p class="first">still can be made functional, at the price of <em>creating the db</em> +(this is how we write the db tests):</p> +<pre class="literal-block"> +def import(lines): # this is a pure function! + db = create_db() + out = importer.import(lines, db) + drop_db(db) + return out +</pre> +</li> +</ul> +</div> +<div class="slide" id="functional-design-and-sql"> +<h1>Functional design and SQL</h1> +<ul class="simple"> +<li>SQL makes a good functional language once you remove mutation +(UPDATE and DELETE)</li> +</ul> +<ul class="incremental simple"> +<li>SELECTs are not really different from list comprehensions +(think of LINQ);</li> +<li>if you can use a view, use it (tell my recent experience making legacy +SQL code more functional)</li> +<li>even non-premature optimizations are evil :-(</li> +</ul> +</div> +<div class="slide" id="references"> +<h1>References</h1> +<p><a class="reference external" href="http://www.phyast.pitt.edu/~micheles/scheme/TheAdventuresofaPythonistainSchemeland.pdf">http://www.phyast.pitt.edu/~micheles/scheme/TheAdventuresofaPythonistainSchemeland.pdf</a> +<a class="reference external" href="http://www.haskell.org/">http://www.haskell.org/</a> +<a class="reference external" href="http://clojure.org/">http://clojure.org/</a> +<a class="reference external" href="http://docs.python.org/howto/functional.html">http://docs.python.org/howto/functional.html</a> +<a class="reference external" href="http://www.defmacro.org/ramblings/fp.html">http://www.defmacro.org/ramblings/fp.html</a> +<a class="reference external" href="http://www.cs.cmu.edu/~rwh/theses/okasaki.pdf">http://www.cs.cmu.edu/~rwh/theses/okasaki.pdf</a> +<a class="reference external" href="http://srfi.schemers.org/srfi-41/srfi-41.html">http://srfi.schemers.org/srfi-41/srfi-41.html</a> +<a class="reference external" href="http://funcall.blogspot.com/2009/05/you-knew-id-say-something-part-iv.html">http://funcall.blogspot.com/2009/05/you-knew-id-say-something-part-iv.html</a></p> +</div> +</div> +</body> +</html> diff --git a/pypers/pycon10/talk.txt b/pypers/pycon10/talk.txt new file mode 100644 index 0000000..181ba14 --- /dev/null +++ b/pypers/pycon10/talk.txt @@ -0,0 +1,487 @@ +Introduction to Functional Programming +=============================================== + +:Event: PyCon Italia 2010 +:Presenters: Michele Simionato, Nicola Larosa +:Date: 2010-05-08 + +.. include:: <s5defs.txt> +.. footer:: PyCon Italia 2010 + +My goals for this talk +------------------------------------------------------ + +- this is an introductory talk (no monads, no combinators, + no pattern matching, no concurrency, no blurb) +- mostly about Python, with 1 or 2 examples in Scheme +- a first part for purists (rebinding and TCO) +- a second part focused on functional *design* in real life +- also a few remarks about functional aspects in automatic testing + and database programming + +First: two misconceptions +---------------------------------------- + +- two extreme viewpoints: + + 1. FP means map, filter, reduce + 2. FP means Haskell + +.. class:: incremental + +- I am in between +- readability and maintainability are my first concerns, not purity +- ... still purity is attractive + +What functional means +------------------------ + +- a lot of emphasis on the functions in the past, nowadays people stress + more the data structures + +- a language is functional if + + .. class:: incremental + + 1. *variables cannot be re-assigned* + 2. *data structures cannot be modified* + 3. *I/O side effects are confined* + +Less is more +---------------------------------------------- + +- FP is about having *less* features, not more! + +.. class:: incremental + +- less ways of shooting yourself in the foot +- the functional programmer is often compared to a monk +- the challenge: can we program under such strong constraints?? + +1: immutable bindings +-------------------------------------- + +- there are no more variables as we intend them +- what about for loops? + +>>> for item in sequence: + do_something(item) + +.. class:: incremental + +- the name ``item`` is rebound at each iteration +- (talk about boxes and labels) +- for loops have worked well for 50+ years, it seems impossible to + live without them +- ... but do for loops work really well? + +Let's see ... +---------------------------------------- + +:: + + root = Tkinter.Tk() + text = Tkinter.StringVar() + label = Tkinter.Label(root, textvariable=text) + menubar = Tkinter.Menu(root) + menu = Tkinter.Menu(menubar) + menubar.add_cascade(label='File', menu=menu) + for item in ['F1','F2', 'F3']: + def showmenu(): + text.set(text.get() + '\n%s' % item) + menu.add_command(label=item, command=showmenu) + root.config(menu=menubar); label.pack() + root.mainloop() + +The usual hack +--------------------------------------- + +:: + + root = Tkinter.Tk() + text = Tkinter.StringVar() + label = Tkinter.Label(root, textvariable=text) + menubar = Tkinter.Menu(root) + menu = Tkinter.Menu(menubar) + menubar.add_cascade(label='File', menu=menu) + for item in ['F1','F2', 'F3']: + def showmenu(item=item): + text.set(text.get() + '\n%s' % item) + menu.add_command(label=item, command=showmenu) + root.config(menu=menubar); label.pack() + root.mainloop() + +This is still a hack +-------------------------------------------------- + +- looking at the interaction of for-loops and closures is a good + test to see if a language is functional + +.. class:: incremental + +- this is an example of the difference between a truly functional language + and an imperative language with some support for FP +- Python, Common Lisp, Go do *not* pass the test +- Haskell, Scheme, Clojure, Perl *do* pass the test + +A simpler example +---------------------------------------------- + +:: + + lst = [] + for i in 0, 1, 2: + def f(): + print i + lst.append(f) + + lst[0]() #=> 2 + lst[1]() #=> 2 + lst[2]() #=> 2 + +A partial solution +--------------------------------- + +:: + + lst = [] + def append_f(i): + def f(): + print i + lst.append(f) + + for i in 0, 1, 2: # mutation is still there + append_f(i) + + lst[0]() #=> 0 + lst[1]() #=> 1 + lst[2]() #=> 2 + +The question +--------------------------------- + +We need a way to compute a loop *without* rebinding the loop variable:: + + def loop123(): + for i in 1, 2, 3: + <do_something(i)> + + loop123() + +Clearly we need to introduce a new scope at each iteration + +The answer: recursion +--------------------------------- + +Convert the function containing a loop in a recursive function with an +additional argument (the loop index):: + + def loop123(i=1): + if i > 3: + return + else: + <do_something(i)> + loop123(i+1) # tail call + + loop123() + +Another example: enumerate +------------------------------------ + +Here is an example of a for loop used to build a data structure:: + + def enumerate(values, i=0, acc=()): + if i >= len(values): + return acc + else: + return enumerate( + values, i+1, acc + ((i, values[i]),)) + +The trick is to use an (immutable) accumulator + +The importance of tail calls +------------------------------------------------ + +- in a truly functional language all for-loops are implemented as + recursive functions in tail call form + +.. class:: incremental + +- the compiler is able to recognize them and avoids creating + a new stack per each iteration (TCO): no recursion limit +- for the programmer POV the loop variable is never reassigned +- it seems *perverse* but it is essential +- expecially for things like pattern matching + +A note on pattern matching +------------------------------------ + +Pattern matching is used a lot in functional languages; in Scheme is +especially used in macro programming; in ML and Haskell is used everywhere, +even to define functions:: + + fun fact n = fact' (n, 1) + and fact' (0, acc) = acc + | fact' (n, acc) = fact' (n-1, acc*n); + +In Erlang is used to destructure the messages sent to the Erlang lightweight +processes. + +TCO and debugging +--------------------------- + +There has been some heated debate last year due to Guido's dismissal +of TCO. + +.. class:: incremental + + In all fairness I must notice that TCO does not prevent debugging:: + + ;; tco.ss + (let loop ((x -3)) + (/ 1 x) + (loop (add1 x))) + + (but I agree that iterative is better than recursive) + +The pragmatist voice +--------------------------------- + +*Python programs written in functional style usually won’t go to the +extreme of avoiding all I/O or all assignments; instead, they’ll +provide a functional-appearing interface but will use non-functional +features internally. For example, the implementation of a function +will still use assignments to local variables, but won’t modify global +variables or have other side effects.* -- Andrew Kuchling + +Example: collecting objects +------------------------------ + +:: + + def collect_mail(mails): + storage = {} + for mail in mails: + client = mail.getclient() + kind = mail.getkind() + date = mail.getdate() + stored = storage.get((client, kind)) + if stored is None or stored.date < date: + storage[client, kind] = mail + return storage + +For purists at all costs ... +----------------------------------------------------- + +- you can avoid mutation of the storage by introducting a functional + update utility:: + + def update(d, k, v): + newd = d.copy() + newd.update({k : v}) + return newd + +... use a helper function +---------------------------------------- + +:: + + def _collect_mail(storage, mail): + # a left-folding function: acc,obj->new-acc + client = mail.getclient() + kind = mail.getkind() + date = mail.getdate() + stored = storage.get((client, kind)) + if stored is None or stored.date < date: + return update( + storage, (client, kind), mail) + else: + return storage + +... then reduce is your friend +------------------------------------- + +:: + + def collect_mail(mails): + return reduce(_collect_mail, mails, {}) + +.. class:: incremental + +- except that you should not be *perverse* +- there is no reason to be functional at any cost if you are using a + non-functional language +- do not fight the language, flow with it +- and this close the discussion about immutable bindings + +2: immutable data +------------------------------------------------- + +- we cannot really get rid of all mutable objects but + certainly mutable objects are overused + +.. class:: incremental + +- mutable objects are often evil + (globals, example of nosetests changing my sys.path) +- mutable objects can be often avoided + (functional update may substitute mutation) +- there are smart functional data structures nowadays + (not much in Python) +- Python has only strings, (named)tuples and frozensets + +namedtuple +-------------------------------------- + +>>> from collections import namedtuple +>>> Point = namedtuple('Point', 'x y') +>>> p1 = Point(0, 0) +>>> p2 = p1._replace(x=1) +>>> p2 +(1, 0) +>>> p1 is p2 +False + +(requires copying the full structure in Python). Python also lacks +immutable dictionaries (say Red/Black trees). + +Generators are mutable +-------------------------------------- + +Many Python programs (especially the ones using list/generator +comprehension and the itertools module) are considered in functional +style even if they are not functional from a purist POV: + + >>> it123 = iter([1, 2, 3]) + >>> for i in it123: print i, + ... + 1 2 3 + >>> for i in it123: print i, + ... + +The functional way: streams +------------------------------------- + +Looping over a stream does not mutate it:: + + > (import (srfi :41 streams)) + > (define str123 (stream-range 1 4)) + > (stream-for-each display str123) + 123 + > (stream-for-each display str123) + 123 + +Yet another difference between Python and a true functional language + +3: confined side effects? +------------------------------------------------- + +- to be able to distinguish pure functions from + impure functions is important:: + + @memoize + def do_something(): + result = long_computation() + log.info('Computed %s', result) + return result + +- Haskell is the purest language when it comes to + confining side effects + +Side effects and unittests +------------------------------------------ + +- in Python keeping the distinction between pure and impure functions is a + question of good coding style + +.. class:: incremental + +- common sense tells you that you should decouple I/O from the + business logic +- unit testing is arguably the master path to functional design: + if you want to test a thing, make it a pure function +- if it is difficult to test it is not functional + +Usual recommendations +-------------------------------------- + +All the usual recommendations to make code more testable, such as + +.. class:: incremental + +- avoid side effects (including the ones at import time) +- do not use special variables (damned them!) +- decouple the system i.e. pass objects around +- write libraries, not frameworks +- feel guilty! + +are recommendations to make code more functional. + +Database connections (bad) +----------------------------------------- + +db = DBSession() # singleton + +def read(...): + db.read_data( ... ) + +def write(data, ...): + db.write_data(data, ...) + +if __name__ == '__main__': + db.init(dsn) + +Database connections (good) +----------------------------------------- + +def read(db, ...): + db.read_data( ... ) + +def write(db, data, ...): + db.write_data(data, ...) + +if __name__ == '__main__': + db = DBSession(dsn) + +FP and databases +---------------------------------------------------------- + +- operating on a DB looks like the least functional thing you can do + (ex. ``importer.import(fileobj, db)``) + +.. class:: incremental + +- still can be made functional, at the price of *creating the db* + (this is how we write the db tests):: + + def import(lines): # this is a pure function! + db = create_db() + out = importer.import(lines, db) + drop_db(db) + return out + +Functional design and SQL +---------------------------------------------------------- + +- SQL makes a good functional language once you remove mutation + (UPDATE and DELETE) + +.. class:: incremental + +- SELECTs are not really different from list comprehensions + (think of LINQ); +- if you can use a view, use it (tell my recent experience making legacy + SQL code more functional) +- even non-premature optimizations are evil :-( + +References +------------------------------------------------ + +http://www.phyast.pitt.edu/~micheles/scheme/TheAdventuresofaPythonistainSchemeland.pdf +http://www.haskell.org/ +http://clojure.org/ +http://docs.python.org/howto/functional.html +http://www.defmacro.org/ramblings/fp.html +http://www.cs.cmu.edu/~rwh/theses/okasaki.pdf +http://srfi.schemers.org/srfi-41/srfi-41.html +http://funcall.blogspot.com/2009/05/you-knew-id-say-something-part-iv.html diff --git a/pypers/pycon10/tco.py b/pypers/pycon10/tco.py new file mode 100644 index 0000000..3b7fd34 --- /dev/null +++ b/pypers/pycon10/tco.py @@ -0,0 +1,4 @@ +def loop(x=-3): + 1/x + return loop(x+1) +loop() diff --git a/pypers/pycon10/tco.ss b/pypers/pycon10/tco.ss new file mode 100644 index 0000000..9b7f17a --- /dev/null +++ b/pypers/pycon10/tco.ss @@ -0,0 +1,4 @@ +;; run this with ikarus -d +(let loop ((x -3)) + (/ 1 x) + (loop (add1 x))) |