viernes, noviembre 21, 2008

Poor man's code folding for F#

Code folding is one of those "features" that I practically never use. The reason is that in some tools the feature doesn't play well with the incremental search. More important is that there's some kind of people that think that a 3000 thousand lines file folded into methods, properties and constants is "nicer". (Trust me they exist)

Some days ago I got some kind of Emacs-fever thanks to the Emacs wiki. I set-up the jabber client, fixed that nasty behaviour with the clipboard and discovered the outline-minor-mode.

The outline-minor-mode is basically a mode for hierarchical text folding. I'm not going to spend my time giving an explanation having this

The mechanism behind this mode is so simple but yet so powerful. Is just a regex.. The hierarchy of the folding depends on the size of the regex match.

Let's imagine this simple example:
module Search =

type IQueue<'c,'a> =
{ makeQueue: 'a list -> 'c
empty: 'c -> bool
first: 'c -> 'a
removeFirst: 'c -> ('a * 'c)
insertAll: ('a list * 'c) -> 'c
}


Using something as simple as this:
(setq outline-regexp " *\\(let ..\\|type .\\|module\\)")

We are able to fold the code in different ways:
module Search =

type IQueue...
module Search... 

But also... We can fold every let definition!
For example:
    let treeSearch<'c,'a> 
(succesors: 'a -> 'a list)
(queue: IQueue<'c,'a>)
(goal: 'a -> bool)
(i0: 'a ) =
let rec explore (fringe: 'c) =
if queue.empty fringe then
failwith "Not Found"
else
let (first, fringe) = queue.removeFirst fringe
if goal first then
first
else explore (queue.insertAll (succesors first, fringe))
explore (queue.makeQueue [i0])

Can fold into this:
    let treeSearch<'c,'a> 
(succesors: 'a -> 'a list)
(queue: IQueue<'c,'a>)
(goal: 'a -> bool)
(i0: 'a ) =
let ...
explore (queue.makeQueue [i0])

The best thing is that it plays perfectly good with C-s and C-y. The regex can be extended to manage any computation expression builder, if-then-else, while, for, match-with blocks.

If you want too hook it to the F# mode...
(setq inferior-fsharp-program "~/lib/fsharp/fsi.sh")
(setq fsharp-compiler "~/lib/fsharp/fsc.sh")
(add-hook 'fsharp-mode-hook
(lambda () (outline-minor-mode)
(setq outline-regexp " *\\(let ..\\|type .\\|module\\)")))

martes, noviembre 11, 2008

Copy to Html

I was going to write something completely different but I had some troubles copying the source code to my blog. I Usually use a little script from Steve Yegge for copying the highlighted text from Emacs to Html.

It had some strange trouble converting the RGB colors and it was not setting the values of the default font.

Here is the code: (using the highlighter :D)

(defun html-string-color (face key)
(mapconcat (lambda (x) (format "%.2x" (/ x 256)))
( color-values (face-attribute
(make-face face) key)) ""))

(defun syntax-highlight-region (start end)
"Adds <font> tags into the region that correspond to the
current color of the text. Throws the result into a temp
buffer, so you don't dork the original."

(interactive "r")
(let ((text (buffer-substring start end)))
(with-output-to-temp-buffer "*html-syntax*"
(set-buffer standard-output)
(insert (format "<div style=\"color:#%s; background:#%s\" ><pre>"
(html-string-color 'default :foreground)
(html-string-color 'default :background)))
(save-excursion (insert text))
(save-excursion (syntax-html-escape-text))
(while (not (eobp))
(let ((plist (text-properties-at (point)))
(next-change
(or (next-single-property-change
(point) 'face (current-buffer))
(point-max))))
(syntax-add-font-tags (point) next-change)
(goto-char next-change)))
(insert "\n</pre></div>"))))

(defun syntax-add-font-tags (start end)
"Puts <font> tag around text between START and END."
(let (face color rgb name r g b)
(and
(setq face (get-text-property start 'face))
(or (if (listp face) (setq face (car face))) t)
(setq color (face-attribute face :foreground))
(setq rgb (assoc (downcase color) color-name-rgb-alist))
(destructuring-bind (name r g b) rgb
(let ((text (buffer-substring-no-properties start end)))
(delete-region start end)
(insert (format "<font color=#%.2x%.2x%.2x>"
(/ r 256) (/ g 256) (/ b 256)))
(insert text)
(insert "</font>"))))))

(defun syntax-html-escape-text ()
"HTML-escapes all the text in the current buffer,
starting at (point)."

(save-excursion (replace-string "<" "<"))
(save-excursion (replace-string ">" ">")))


BTW there's some minor issue with the greater than symbol inside of the strings.

Change in the language... For good?

After thinking about it carefully I have decided to start writing my "Programming" blog in English. I don't think I'm going to lose any of the current readers (Is there any reader out there???) but instead, this can be the perfect exercise for improving my horrible English.

It would be really nice if you can point out any nasty grammatical mistake or misuse in the words (I have a really a bad time with some phrasal verbs)

viernes, octubre 17, 2008

Números Normales

Últimamente ando pensando en números normales.

Cojamos 2:

El número de Champernowne:
0.123456789101112131415...
La constante de Chaitin
0.00787499699...

Dicen que Pi es normal... Si Pi es normal quiere decir que 1, 12, 123, 1234, 12345, 123456 están contenidos en Pi.

Si pi es normal 0, 007, 00787499699 están contenidos en pi.

Si tomamos un numero de tamaño n arbitrario substring de Champernowne o la constante de Chaitin estará contenido en pi... Y algo que podemos estar seguros es que independiente de si pi es normal o no cualquier substring estará presente en el de Champernowne.

Podemos adentrarnos infinitamente en los substrings de Champertown que contienen substrings de la constante de Chaitin... cadenas sin nombre, cadenas con nombres, fragmentos de pi, pi/2, pi/4...

martes, octubre 14, 2008

Reader Monad en F#

Intro

El proyecto en el que trabajo usa en forma extensiva Linq para realizar todas las operaciones con la base de datos.

Éste es un ejemplo clásico de consulta:(1)


  112 let findById (db: MyProjectClassesDataContext, id) =

  113     SQL <@ {    for c in %db.Products

  114                 when c.ProductID = %id

  115                 -> c } @> |> Seq.hd



El ejemplo compila la expresión a un query nativo de SQL y lo ejecuta. El objeto por el cual nos "comunicamos" con la base de datos es el "DataContext" (En este caso le pusimos un nombre super enterprisey "MyProjectClassesDataContext" para darnos de importantes).

"DataContext" También nos ayuda a realizar cambios en forma transaccional. Por ejemplo:

  117 let changeDescription id text =

  118     let db = new MyProjectClassesDataContext()

  119     let product = findById(db,id)

  120     product.Description <- text

  121     db.SubmitChanges()



Cambia la descripción en forma transaccional.

Si tuviésemos ésta otra definición:


  123 let changeToLiquors id =

  124     let db = new MyProjectClassesDataContext()

  125     let product = findById(db,id)

  126     product.Type <- "Liquors"

  127     db.SubmitChanges()

  128 

  129 let changeDescriptionAndToLiquors id text =

  130     changeDescription id text

  131     changeToLiquors id



La creación de 2 SubmitChanges hace que hallan realmente 2 "transacciones" una puede realizarse con éxito pero la otra puede fallar. En algunos casos esto representa un peligro para la integridad de la base de datos. La forma correcta si queremos atomicidad en la operación sería crear un solo SubmitChanges para ambas operaciones.

Así mismo, los objetos de un DataContext, no pueden ser compartidos entre diferentes DataContext. Si digamos tengo los DataContext A y B, saco un producto de A y hago B.SubmitChanges(); el producto dado que pertenece a A, no es persistido en la base de datos.

Ésto nos obliga a tener un solo DataContext por transacción.

  117 et changeDescription db id text =

  118     let product = findById(db,id)

  119     product.Description <- text

  120 

  121 let changeToLiquors db id =

  122     let product = findById(db,id)

  123     product.Type <- "Liquors"

  124 

  125 let changeDescriptionAndToLiquors db id text =

  126     changeDescription db id text

  127     changeToLiquors db id



Incluso la última función la construimos recibiendo el DataContext como parámetro. Si estamos construyendo una librería ¿Quién somos para decir cuando queremos hacer persistencia? cada SubmitChanges e instanciación de DataContext es un problema si queremos componer después cada una de las funciones.

Pero ahora nos toca pasar SIEMPRE el DataContext, muchas veces toca hacer "types annotations". Y aceptemoslo... escribo a duras penas a 20 WPM. Y los nombres enterprisey no ayudan. A veces con aplicación parcial puede uno lograr ciertos ahorros pero está lejos de ser una solución.

Y bien?

Computations which read values from a shared environment.

Kurva jó!

F# posee azúcar sintáctica parecida a la sintaxis "do" de Haskell.

Definimos la monada(2)


   11 module Reader =

   12     type Reader<'e,'a> = Reader of ('e -> 'a)

   13     let apply (Reader f) r = f r

   14     let returnM x = Reader (fun e -> x)

   15     let bindM m f =

   16         Reader (fun e -> apply (f (apply m e)) e)

   17     let letM v f = bindM (returnM v) f

   18     let delayM f = bindM (returnM ()) f

   19 

   20     (*Reader Monad*)

   21     type MReaderBuilder() =

   22         member b.Return(x) = returnM x

   23         member b.Bind(v,f) = bindM v f

   24         member b.Delay(f) = delayM f

   25         member b.Let(v,f) = letM v f

   26 

   27     let reader = new MReaderBuilder()

   28     let ask = Reader (fun e -> e)

   29     let local f c = Reader (fun e -> apply c (f e))

   30     let asks sel = bindM (ask) (returnM << sel)

   31 



Internamente la monada no es mas que una función desde el "Entorno" hacia un "Retorno". Empecemos con ejemplos sencillos de como usar la monada.

Usando un string como contexto.


   35 let lengthOfString =

   36     reader {let! length = asks String.length 

   37             return length

   38            }



Simplifiquemos el contexto a un simple string. Esta definición nos retorna dado un contexto particular la longitud del mismo.

Para pasar el contexto:


   40 apply lengthOfString "hello"



(Si... retorna 4)

Ahora... A diferencia de Haskell, F# permite Side Effects. De esta manera lo siguiente también es posible(3).


   42 let lengthOfModifiedString =

   43     reader {let! length = lengthOfString

   44             let! lengthModified =

   45                 local

   46                     ((+) "Preffix") lengthOfString

   47             let! env = ask

   48             do printfn "%A" env

   49             return sprintf "%d: %d: %s"

   50                    length lengthModified env

   51            }



"asks" aplica la función al contexto.
"local" crea un contexto modificado(4) mediante una función.
"ask" retorna el contexto o entorno.

De la misma manera podemos usar el reader en casos mas complejos.


  103 type DBReader<'a> =

  104     Reader<MyProjectClassesDataContext, 'a>

  105 

  106 let getClients (db:MyProjectClassesDataContext) =

  107     db.Clients

  108 let getProducts (db:MyProjectClassesDataContext) =

  109     db.Products

  110 let getClientProducts (db:MyProjectClassesDataContext) =

  111     db.ClientsProducts

  112 

  113 let getClientByName1 name : DBReader<Person> =

  114     reader {let! clients = asks getClients 

  115             return List.find (fun (x) -> x.Name = name) clients

  116     }



Él código(5)

(1) Si mal no estoy la forma de realizar este tipo de consultas cambio con el CTP de F#

(2) Él nombre que se le da en F# son "computation expressions"

(3) El "do" para las expresiones que retornan unit creo que ya no es necesario con la última versión de F# (CTP Release).

(4) Toca recordar que estos es útil tratando con estructuras inmutables (i.e string).

(5) No usa la ultima versión de F#

miércoles, octubre 08, 2008

Orgullo universitario

Misión

"La Universidad EAFIT tiene la Misión de contribuir al progreso social, económico, científico y cultural del país, mediante el desarrollo de programas de pregrado y de postgrado -en un ambiente de pluralismo ideológico y de excelencia académica- para la formación de personas competentes internacionalmente; y con la realización de procesos de investigación científica y aplicada, en interacción permanente con los sectores empresarial, gubernamental y académico."


En letras pequeñas... Pluralismo ideológico incluye pero no está limitado a: descuentos en teléfonos móviles, tarjetas de crédito y tiquetes aéreos.



viernes, agosto 01, 2008

Dejando trabajo para después

Hace poco tuve un pequeño problema con el proyecto que estoy realizando. Se trataba de lo siguiente.

Existía una tarea que debía realizarse en forma transaccional (agregar unos 4 o 5 registros en una base de datos que estaban relacionados) y después era necesario mover unos archivos a la posición indicada. El path de estos archivos dependía del campo id de la base de datos y este solo se conocería cuando se completara toda la transacción. (ejecución de SubmitChanges() )

En general, el "patrón" de problema es el siguiente:

Existen t1, t2 ... tn tareas. Estas tareas necesitan realizar trabajo adicional pero debe realizarse después de un evento.

Siendo asi, se me ocurrió esta idea. Tipar estas tareas que necesitan trabajo extra como:


    1 unit -> unit -> unit


En cristiano... cuando se ejecuta la tarea devuelve una función que requiere ser ejecutada.

El ejemplo de coquito que hice para el blog es el siguiente:


    6 let location = ref "workplace"

    7 

    8 let dayWork s () =

    9     printfn "Work #%A in %s" s !location

   10     fun () -> printfn "Home work #%A in %s" s !location

   11 

   12 let works  = [for i in 1..5 -> dayWork i]



Existen 5 trabajos del día. Cada trabajo del día conlleva trabajo que queda de tarea para la casa. Pero para realizar este trabajo resulta necesario primero "ir a la casa"

Este evento global lo represento con la celda de memoria "location". En el caso que mencione anteriormente el evento que se requiere para terminar es un SubmitChanges()
(ir a casa es básicamente ésta función)


   19 let goHome () = location := "home"



Ahora bien... la idea es implementar un mecanismo que ejecute las tareas, luego el evento y luego todo el trabajo de las tareas ejecutadas que requería del evento.

Al principio pensé en implementar una "computation expression" pero es mas fácil de lo que imagine... el viejo fold_left

    4 let nop = (fun () -> ())



   14 let finish after this  =

   15     let afterWork =

   16         List.fold_left (fun x y -> x >> y() ) nop after

   17     this()

   18     afterWork()



Lo que estamos diciendo con el fold es lo siguiente.

Tenemos una operación nop. Ejecute la primera tarea y luego componga el trabajo por hacer con el nop anterior. y haga esto para toda la lista de tareas. al final el fold habrá ejecutado todas las tareas y habrá acumulado una funcion con todo el trabajo por hacer.

Ejecutamos el evento necesario. Y ejecutamos lo que requería de este evento (El resultado del fold).

Aprovechándose uno de la sintaxis puede uno hacer esto:

   22 finish

   23     [dayWork 1

   24     dayWork 2

   25     dayWork 3]

   26     goHome



Los punto y comas no son necesarios en listas que se escriben en varias líneas. Esto le da un "look imperativo" que me agrada :P.

Él resultado:

Work #1 in workplace
Work #2 in workplace
Work #3 in workplace
Home work #1 in home
Home work #2 in home
Home work #3 in home

miércoles, julio 30, 2008

Acerca de Linq

"Vos si te llenas de mocos por bobadas"... Eso me dijo alguien cuando le dije que necesitaba responder al comentario de un amigo en Twitter. Y si... lleno de mocos y todo empiezo. :P

Pues bien. Es básicamente lo siguiente:

febuiles http://xrl.us/ok5v2 "The idea that you can express your intent in terms of higher-level query expressions is really new" - Nope, sorry.

diegoeche @febuiles porque no es nueva?

febuiles @diegoeche: Twitter no es el medio: Tumblr

Y el post es básicamente esto:

var products = from p in db.Products
where p.Category.CategoryName == "Beverages"
select p;

products = Products.find_by_category_name("Beverages")

Los queries que abstraen condiciones de un nivel inferior (en este caso SQL) no son una idea nueva. Los dos ejemplos anteriores hacen lo mismo y solo difieren en sintaxis.


Decir que Linq es ultra-novedoso es no saber medir el adjetivo "ultra-novedoso". Linq es 90% Marketing y 10% buenas ideas... el que no se haya dado cuenta de eso es un MVP cerebri-lavado.

No comparto la idea de Eric Meijer, sin embargo creo que el ejemplo está muy mal escogido.

En primer lugar no consideraría llamar una función con un nombre bonito un DSL. De hecho me parece sobre simplificar el termino llamar DSL a cualquier conjunto de identificadores bonitos dentro de una sintaxis externa para una tarea específica. De la misma manera me parece erróneo llamar a "está función con nombre bonito" algo equivalente "higher-level query expressions".

La sentencia de Linq después de de-sugaring es algo como:

db.Products.Where(p => p.Category.CategoryName == "Beverages").Select(p => p);
(Probablemente por usar la proyección de identidad se omita el último Select)


Eso no es nuevo... reemplaza uno filter por Where y map por Select y es algo tan viejo como Lisp (de pronto anterior?) y me parece muy de "alto nivel". Lo que creo que se refiere Meijer como "nuevo" es precisamente el de-sugaring (que por lo que tengo entendido va pasando la la variable "p" por medio de una monada).

Así que cuando dice "Los dos ejemplos anteriores hacen lo mismo y solo difieren en sintaxis." Resulta un comentario vacuo.

Uno puede pasar funciones en Java usando clases anónimas de un solo método y puede decir uno "Java hace lo mismo que haskell... solo difiere la sintaxis"

O acaso un foreach no es nuevo comparado con un for??
Ambos hacen lo mismo... difieren en la sintáxis... y si encapsulo el for en una función que lo haga ver bonito? la inclusión del foreach en un lenguaje de programación como elemento sintáctico deja de ser nueva? no.

Pero es precisamente ésto a lo que se refiere Eric Meijer. Incluir sintaxis adicional en el lenguaje con el fin de operar con colecciones es "nuevo". Que tan nuevo? tan nuevo como Linq? la verdad no sé.

Y novedoso? Pues uno usando la monada de lista de haskell puede hacer lo que hacen las Query Expressions.

Ahora bien... se que es estúpido escribir en vez de una refutación un "el argumento que usaste es muy malo" pero hace rato no escribía.

jueves, mayo 29, 2008

Batalla de los Ewoks?

En un url muy muy lejano...

Pues el último escrito de Steve Yegge generó todo tipo de reacciones:

Return of Statically Typed Languages
Guide You the Force Should
Revenge of the Statically Typed Languages
A New Hope - Polyglotism

Y me imagino que en este momento deberán haber otros cientos de entradas similares. Dentro de "la saga", la opinión con la que mejor me llevo es con la de "Ola Bini"

I guess that's the message of this post. Compare languages, understand your most important tools. Have several different tools for different tasks, and understand the failings of your current tools.


Lo cual va muy de la mano con otro comentario de Ted Neward:

Erik Meijer coined this idea first, and I like it a lot: Why can't we operate on a basic principle of "static when we can (or should), dynamic otherwise"?


Un algunos de ejemplos que se me ocurren podrían ser estos:

Uno de los fuertes por ejemplo dentro de los lenguajes dinámicos ha sido procesamiento de texto. Si uno se remonta a la historia puede uno darse cuenta que Lisp, Perl, Python han sido muy fuertes en este campo. Quien necesita tipos para hacer búsquedas y reemplazos en texto?

De la misma forma puede uno llegar a dominios de Problemas donde un lenguaje estaticamente Tipado sea mejor. Si necesito armar un árbol sintáctico y necesito hacer verificaciones... es bueno hacerlas en runtime? no es acaso mejor que el sistema de tipos me ayude con eso? Es por eso que a la hora de hacer compiladores mucha gente opta por Sistemas fuertemente tipados como Haskell y derivados de ML. Las propiedades de verificación de este tipo de lenguajes supera en forma enorme la de los lenguajes dinámicos(1).

Si quiero estar haciendo modificaciones de código en caliente para que me voy a complicar con tipos? Invito a cualquier persona a intentar simular comportamientos dinámicos como agregar-modificar métodos en runtime usando las capacidades de "Reflexion" de Java o C#. Y luego intente hacer lo mismo en Ruby. Estoy seguro que la elección del sistema de tipos de Erlang tiene mucho que ver con este punto.(2)

Consejo:
Sea crítico con este tipo de Flame-Wars. Muchas veces todos comparten cierta razón en lo que dicen. Y al final siempre se aprende. (Parecen inaportantes los Ewoks y al final...)

(1) Las posibilidades de los ML y Haskell para verificación no solo tienen causas en el sistema de tipos fuertemente tipados. El hecho de poseer transparencia referencial por ejemplo ayuda

(2) "Reemplazo de código en caliente" debe leerse a la ligera! El caso de capacidades dinámicas (como cambiar métodos) Es muy diferente lo que hace Erlang. Pero la idea es hacer referencia a ese tipo de comportamientos.

lunes, mayo 26, 2008

Recordar es vivir

Recuerdo que el primer lenguaje "declarativo" con el que empecé (mejor digamos... traté) fue Prolog. Me acuerdo que en aquella época no fui capaz de hacer siquiera un Fibonacci.

Hace algunos días me dió curiosidad cuando un compañero del trabajo me contó la historia de lo difícil que le fue encontrar un error de sintaxis (un punto en lugar de una coma) y No me aguante las ganas de bajar SWI Prolog y probarlo.

Hice solo un par de bobadas, y confieso que me costó un poco "acomodar el cerebro" con el modo de Prolog para expresar la recursión.

Fibonacci:


fib(0,1).
fib(1,1).
fib(N,FIB):-
N > 1,
N1 is N-1,
N2 is N1-1,
fib(N1,FIB1),
fib(N2,FIB2),
FIB is FIB1 + FIB2.


Sumatoria de una lista:


sum([],0).
sum([X|XS],A) :-
sum(XS,A1),
A is X + A1.


Sé que no son los ejemplos mas interesantes del mundo, pero la verdad me emocione cuando volví a leer el término "Clausula de Horn" en el tutorial.

Que piensan de la sintaxis comparada con la de los lenguajes funcionales usuales?
Alguien sabe que paso con los lenguajes Lógicos? En un video (si mal no recuerdo) Eric Meijer decía algo sobre cierta desilusión del paradigma lógico (creo que específicamente hablaba de que las cosas bacanas requería intervenir el backtracking o algo así)

Y que hay de la pureza?

jueves, mayo 15, 2008

Hoy aprendí:

Hay un blog que me gusta mucho y ese es Tumbolia Para el que no sepa, Tumbolia es el mundo de la inconsistencia que describe Douglas Richard Hofstadter En Gödel Escher and Bach. La estructura del Blog es simplemente, de vez en cuando mencionar una lista de cosas nuevas que uno aprendió.

Tengo ganas de hacer lo mismo de vez en cuando... así termine siendo esto un copy-paste de Slashdot y Reddit.

Y empecemos!

* La décima regla de Greenspun establece:


"Any sufficiently complicated C or Fortran program contains an ad hoc, informally-specified, bug-ridden, slow implementation of half of Common Lisp"


* El sistema de Chat de Facebook está montado sobre Erlang. La razón es (sobre-simplificando) que Erlang permite procesos extremadamente ligeros.

* Según este blog es posible crear algorítmicamente palabras que parezcan escritas en otro idioma.

"...that if you compile a table of the relative frequencies of three-letter sequences (trigraphs) in English text, and then generate random text with the same trigraph frequencies"


Tengo ganas de hacer el experimento... El follow up contiene detalles de como hacerlo. (Alguien dijo hash tables? :P)

* Un libro que espero algún día comprar es Purely functional data structures Él autor tiene un también un blog que me parece muy interesante. No sabía que el "miedo a los arreglos" y el uso de Pseudo-Arreglos cuando las personas empiezan a programar es algo común.

Ese código con variables de la forma a1 a2 a3 se me hizo muy familiar :P

* Brace expansions en bash... un ejemplo vale mas que mil palabras:

Que hace?

diegoeche@multivac:\ mkdir {1,2,3,}log

lunes, mayo 05, 2008

Get the facts

Creo que esto es cuento viejo pero mi maldita personalidad me impide leer una cosa de éstas y quedarme "quieto"

www.getthefacts.com es una pagina de Microsoft donde se "ayuda" a realizar una buena decisión a la hora de escoger la plataforma para el server. Hay una pequeña pestaña de "Compare Windows to Linux" y simplemente da vergüenza la falta de inteligencia de ésta campaña. Empecemos por la metodología:

Es muy sencillo, existen varias pestañas con temas como "How Windows Reduces TCO (Total Cost Ownership)", "Understanding Platform Reliability" "The Real Story on Security" Cada pestaña incluye una gráfica que solo puede convencer al mas ingenuo de los ingenuos, un pequeño texto explicativo y "Casos de estudio".

How Windows Reduces TCO

Tengo mis dudas sobre el famoso 7% del costo del software (licencias) pero la verdad no tengo nada contundente que decir. Así que cojamos un caso de estudio cualquiera.

Speedy Hire

Contexto:
"Since its creation in 1977, the group has expanded into different markets and sectors, largely by taking over competitors such as, most recently, the tool-hire division of Hewden Stuart. These successful takeovers left Speedy Hire at the head of unreliable, heterogeneous software environments, all running independently from one another."

Solución:
Speedy Hire decided to switch to a Microsoft environment across the board. The first stage was to change the hire application to Microsoft Dynamics™ AX, in what was one of the organization's biggest IT projects to date. As part of this project, Speedy Hire replaced all of the computers in its depots, replacing Linux personal computers with Wyse V90 Winterms running Windows XP Embedded, and open-source office products with the 2007 Microsoft Office system.

Wow... estos tipos en serio se ganan el premio! Cambian un ambiente heterogeneo a uno homogeneo usando windows y las conclusiones son obvias. "Windows es mejor!" "Windows ahorra dinero". Una plataforma homogénea ahorra dinero sin importar por que sistema se opte y porque? porque se necesita menos conocimiento en IT, y eso usualmente es equivalente a tener menos personas dando soporte.

Pero momento... no era acaso también esta una evaluación a nivel de plataforma de server? porque se utiliza este caso de estudio? es relevante? NO

Understanding Platform Reliability

Empecemos por la brillante deducción:
"Organizations define reliability as more than uptime. A reliable solution is one that is:
* Easy to configure and maintain
* Predictable, especially as business requirements evolve
* Therefore, available to end users. "

Hace falta el:



Caso de estudio: Western Materials

Pues resulta que ellos usaban Nitix y también tienen casos de estudio.

Y obvio... como estamos hablando de Reliability:

“Nitix wouldn’t support anything newer than Outlook 2000. Anyone using Outlook 2003 had to manually maintain two Inboxes on the desktop and use a rule to move messages from one Inbox to the other.”

Suena música de X-Files, porque como no se absolutamente nada del mundo real me pregunto... ¿Que tiene de diferente Outlook 2003? alguien que me responda! yo estaba seguro que era cuestión de configurar el protocolo adecuado (IMAP, POP3) que me hace falta?

The Real "Story" on Security

Ve uno una gráfica al lado derecho con unas barras: El típico encargado de IT dice "Oh veo unas barras de colores!! La de windows vista es la mas pequeña!! Instalemos vista! ergo, mas seguridad!!" (añadir el tono de imbécil)

La versión con la que comparan Linux es Ubuntu 6.06 de Escritorio. (Contra vista y XP) pero momento. No es ésta acaso la página para escoger mi plataforma server? Porque comparan los sistemas de escritorio? Y bueno... sigamos con la comparación. Cuantos paquetes trae Ubuntu vs Windows por Default? Supuestamente se trata de una versión reducida PERO si uno mira el reporte en el que está basado:

"I manually excluded gimp and OpenOffice from the package list. I didn’t exclude anything
else because I felt that most users would not go to the effort to manually remove packages
from the default desktop installation."

Wow... eso si que hace la diferencia!

Ahora las métricas:

Entre tantas se destaca una que se lleva fácilmente el premio a "la falacia de pez rojo" mas grande de la historia. La métrica de la que se sienten super orgullosos es el número de "patch events" pero... que tiene que ver el numero de patch-events con la seguridad?

Otra cosa interesante es que se mide "número de vulnerabilidades encontradas" y "número de vulnerabilidades de éste conjunto que fueron arregladas" Él número de vulnerabilidades en Vista y XP es mucho menor que en Ubuntu, PERO eso qiere decir que Ubuntu sea mas inseguro?

La falacia con la que pretende razonar Microsoft es una especie de proporcionalidad entre vulnerabilidades existentes vs vulnerabilidades encontradas. Al decir que en Vista se han encontrado menos vulnerabilidades se pretende llevar al lector a pensar "si encuentran menos es porque hay menos" lo cual resulta totalmente falso. Una vulnerabilidad es en esencia una característica del software que en alguna circunstancia puede dar una oportunidad al atacante de aprovecharla. (Desarrollar un exploit) Én el caso de Vista y XP encontrar una vulnerabilidad usualmente se basa en técnicas de ingeniería inversa.

Que es más facil, encontrar una vulnerabilidad de Buffer overflow en él código C con comentarios de Ubuntu 6.06 con reportes técnicos y demás. O en él código objeto Hexadecimal de Windows Vista justo después de su release sin ninguna documentación?

Ahora bien. La métrica que prefiero utilizar... usando los mismos datos es "porcentaje de Vulnerabilidades Arregladas"

Veamos las gráficas:



En Windows Vista es alrededor de un Ratio 50/50 una vulnerabilidad la arreglan, otra no. Muy perezosos para mi gusto la verdad... Porque con todo el dinero de microsoft y con lo costoso de una licencia tienen ese horrible ratio de vulnerabilidades arregladas?

(será que internamente usan una moneda y todo?)

y en Ubuntu? Usando el método súper científico de medir con el dedo sobre la pantalla el gráfico me da que el 95% de las vulnerabilidades fueron arregladas.

martes, abril 15, 2008

Convertir de CSV a String Array

La primera vez pensé que era cuestión de

File.ReadAllLines(Filename).Map(\x -> Split(","))

Pero eso no cubre los casos "raros". Y eso es que según la wiki y mas específicamente la RFC 4180 esta el caso de poder incluir separadores dentro de una entrada. Poder incluir saltos de línea e incluir comillas dobles (").

En F# resulta muy fácil. Uno solo hecha mano de FSLex y FSYacc.

La definición del lexer:


    8 rule token = parse

    9 | ',' {SEPARATOR}

   10 | '"' {QUOTE}

   11 | '\n'| '\r' '\n' {NEWLINE (Lexing.lexeme lexbuf)}

   12 | eof {EOF}

   13 | _ {OTHER (Lexing.lexeme lexbuf)}



El Parser:


   17 start:

   18     Line  {$1}

   19 

   20 Line:

   21     Entry  {[$1]}

   22     | Entry SEPARATOR Line  {$1::$3}

   23 

   24 Entry:

   25     | Text                   {$1}

   26     | QUOTE QuotedText QUOTE {$2}

   27 

   28 Text:

   29                         {new System.Text.StringBuilder()}

   30     | Text OTHER        {$1.Append($2)}

   31 

   32 SubField:

   33     | OTHER                {$1}

   34     | SEPARATOR            {","}

   35     | NEWLINE               {$1}

   36 

   37 QuotedText:

   38                             {new System.Text.StringBuilder()}

   39     | QuotedText SubField    {$1.Append($2)}

   40     | QuotedText QUOTE QUOTE {$1.Append(@"""")}



Y listo.

Haciendo esto me tropecé con unas cosas que se me había olvidado que existían.

Reduce-Reduce conflict y Shift-Reduce conflict. Inicialmente estaba haciendo parsing de todo el archivo. Pero que pasa si la primera línea está mala? mejor hacer el trabajo bajo demanda (Record por Record). Eso resuelve Los conflictos
Uno hace un Wrapper de 10 líneas y después puede uno generar una Secuencia (una especie de Generador, o lista por comprensión)


   26 let readCSV (filename:string) encoding =

   27     seq {use text = new StreamReader(filename)

   28         let csv = new CSVParser(text, encoding)

   29         while not csv.EndOfStream do

   30             yield csv.ReadRecord()

   31     }



Quiero verlos haciendo esto en Java!!

martes, abril 08, 2008

Parentheses are evil

A veces la legibilidad del código es subestimada, sin embargo es tal vez de las cosas mas importantes.

Últimamente he estado trabajando mucho en F# y me he topado por accidente con pedazos código que me gustan no por lo elegantes sino por su legibilidad.

Digamos que tenemos un código en un pseudo-Java-C#


   35 public static IElementsContainer logout(IElementsContainer ie){

   36   IElementsContainer logout = Helpers.elementById("LoginStatus1", ie);

   37   if(logout.Exists){

   38     logout.click();

   39   }

   40   return ie;     

   41 }



Y lo comparamos con esto:


   38 let logout ie =

   39     let logout = elementById "LoginStatus1" ie

   40     if logout.Exists then click logout

   41     ie



Me voy a hacer el bobo con el ahorro en caracteres. Pregunto... ¿Soy el único al que le parece la linea 40 del segundo ejemplo hermosa?

Esta linea es posible solo porque en F# como en muchos lenguajes funcionales, se utilizan funciones a la Curry. Se podría reproducir el efecto en lenguajes imperativos como Ruby dado que los paréntesis resultan opcionales. El ejemplo anterior es sencillo, pero no es nada sorprendente.

Ahora un caso típico, Digamos que tenemos un objeto que posee un estado y que debemos realizar una serie de operaciones sobre este que cambian su estado. Y supongamos que el orden es importante. Las funciones retornan una referencia al objeto modificado.

Esta sería la forma cerda:


   40 

   41 public IElementContainer login(String user, String passwd,

   42                                IElementContainer ie){

   43 

   44     return clickById("Login1_LoginButton",

   45             typeById("Login1_Password", passwd ,

   46             typeById("Login1_UserName", user ,

   47               logout(ie)));

   48 

   49 }



Legibilidad Nula. Pero digamos que el que escribió eso se sintió bien porque "se ahorro los punto y comas":

Ahora hagamoslo mas "imperativo".


   41 public IElementContainer login(String user, String passwd,

   42                                IElementContainer ie){

   43     ie.logout(ie);

   44     ie.typeById("Login1_UserName",user);

   45     ie.typeById("Login1_Password", passwd); 

   46     ie.clickById("Login1_LoginButton");

   47     return ie;

   48 }



Digamos que legible. Y F#? podemos implementar algo parecido a la primera solución pero que se deja leer en el mismo sentido que ocurren los sucesos.


   41     let login user passwd ie =

   42         logout ie 

   43         |> typeById "Login1_UserName" user

   44         |> typeById "Login1_Password" passwd 

   45         |> clickById "Login1_LoginButton"



Él operador |> es como el pipe de shell. El resultado se lo pasa a la funcion de la derecha.

<3


PD: Los ejemplos son sacados de mu ultima experiencia trabajando con F#, Watin y NUnit. WatiN es un API para Testing Funcional (Funcional de Funcionalidad no de FP). Pese a la poca documentación de WatiN lo recomiendo en forma enorme.

sábado, marzo 29, 2008

En 100 años?

Hace poco leí un ensayo de Paul Graham llamado "The Hundred-Year Language" Me pareció muy interesante.

Una de las cosas que quisiera discutir es ésta:

I think that, like species, languages will form evolutionary trees, with dead-ends branching off all over. We can see this happening already. Cobol, for all its sometime popularity, does not seem to have any intellectual descendants. It is an evolutionary dead-end-- a Neanderthal language.

I predict a similar fate for Java. People sometimes send me mail saying, "How can you say that Java won't turn out to be a successful language? It's already a successful language." And I admit that it is, if you measure success by shelf space taken up by books on it (particularly individual books on it), or by the number of undergrads who believe they have to learn it to get a job. When I say Java won't turn out to be a successful language, I mean something more specific: that Java will turn out to be an evolutionary dead-end, like Cobol.


Porqué habría de ser Java un Dead-End Evolutivo? La respuesta es:

Java cambia muy lentamente!

No se trata de los problemas inherentes al boilerplate para pasar una función por parámetro. No se trata de que los namespaces sean incómodas carpetas ni tampoco que no haya inferencia de tipos.

Se trata de que estos problemas no se solucionan. Se toman años en aprobar una JSR, pero entonces?

Que pasará con Java en los próximos 100 años? Cuantos años nos aguardan, antes de que Java quede confinado a los cores de "las grandes compañías", que en algún momento pensaron que Cobol y RPG eran los mejores lenguajes?

Mas interesante aún! Cual será la sucesión de lenguajes de los próximos 100 años? Que nos espera después de Java?

Muchas preguntas, no?

Precisamente, otro artículo de Paul Graham (creo que es realmente una cita) da una buena herramienta de como pensar sobre la siguiente sucesión.

Empecemos a Especular!

Hay como dos lados, por uno creo que se dió un estilo de transición de la forma:

Arreglemos el "noun oriented thinking" del lenguaje de programación X

(Donde X es Java, C#)

Con C# ya medio se arreglo con la inclusión de lambdas (aunque con muchas limitantes) y en Java ahí está la propuesta. Los lenguajes que superen esta prueba tal vez continúen siendo ramas gruesas dentro de el árbol evolutivo.

Él otro lado tiene mucho que ver con los lenguajes dinámicos. Creo que el próximo lenguaje de entre toda esa familia (Groovy, Ruby) será uno que mejore el performance de estos. De hecho, los primeros esfuerzos se comienzan a observar con el montón de implementaciones para la CLR, DLR y la JVM.

Creo que la posibilidad de integración de DSL's jugará un papel importante. En el momento se que Ruby por ejemplo trata de acomodar su sintaxis para hacer mini-DSL's, muy al estilo de usar nombres de métodos dicientes y literales de la forma conveniente como en SmallTalk, pero le hacen falta capacidades reflexión sobre sus construcciones sintácticas. C# intenta hacer algo sobre esto, limitado a "expresion trees" sobre sus lambdas. F#, pese a estar lejos de ser un First Class Citizen en .Net tiene algunas posibilidades en este aspecto, pero hace falta una función Compile (como lo hay en los expresion Trees). No hablemos de Java, éste no posee nada de esto.

No se hasta donde nos pueda llevar la teoría de tipos, pero de algo estoy seguro. Creo que algunos lenguajes imperativos tendrán clases de tipos como Haskell y una buena inferencia que haga generalización automática. Construcciones para manejar paralelismo, etc...

Lo mas interesante de todo ésto es que todas estas ideas ya existen desde Lisp. Será que surge algo Realmente nuevo?