domingo, 15 de noviembre de 2015

6. No me dejan imprimir... (o "I/O considered harmful")

Las razones por las que NO se debe incluir E/S en un curso inicial.


  Una característica fundamental de Gobstones es que carece completamente de comandos de "entrada/salida" (o sea, comandos para leer información del teclado y para imprimir información por una pantalla -- excepto en el modo interactivo, pero ese es tema de otro post). Esta es una característica totalmente intencional.

   La razón fundamental para esta decisión tan controversial tiene que ver con la manera en que queremos que los programas sean comprendidos, y con una forma particular de pensarlos: según su naturaleza denotacional.
   Como hablamos en "La importancia de empezar por los procedimientos", los programas tienen dos formas de ser comprendidos:
  1. la *naturaleza operacional*, o sea, qué es un programa desde el punto de vista de la máquina
  2. la *naturaleza denotacional*, o sea, qué es un programa desde el punto de vista de las personas que lo leen.

   La naturaleza operacional permite entender a un programa como una secuencia de instrucciones compleja, y se focaliza en la forma en que el programa ejecuta, las formas concretas en las que la información se representa. Esta forma es compleja de entender para una persona cuando el programa tiene más de unas docenas de líneas, y es normalmente complejo tratar de deducir lo que un programa hace contenmplando solo esta forma. Sin embargo, es útil para, por ejemplo, poder hacerse una idea del tiempo que demandará ejecutar el programa.

   La naturaleza denotacional, por otra parte, permite entender a un programa como una transformación de información, y se focaliza en los efectos que el programa tendrá luego de completar su ejecución. Esta forma es normalmente la que es interesante para las personas, pues permite entender al programa como la solución a un problema (transformar cierta información en otra distinta) y analizar sus características, sin importar lo que las instrucciones individuales hagan. (Para los veteranos de la programación, esta forma se conoce usualmente con la denominación de "programa como caja negra", haciendo alusión a la caja negra de los aviones. Sin embargo creemos que esta denominación es inadecuada y confusa.)

   Volviendo al tema de este post, la posibilidad de poner comandos de entrada/salida (E/S) en un programa hace que el programador se focalice casi exclusivamente en la naturaleza operacional del mismo, olvidando o soslayando la naturaleza denotacional.
   Puesto que el objetivo del enfoque didáctico Gobstones es que los estudiantes de programación adquieran la capacidad de entender a sus programas como transformaciones de información, es necesario que los comandos de E/S no sean parte del repertorio de herramientas a utilizar.

   Para reemplazarlo, las herramientas que implementan Gobstones (PyGobstones, al momento de escribir este post) utilizan como mecanismo de comunicación la visualización del tablero antes de que corra el programa (*el tablero inicial*) y la visualización del mismo después que el programa finalizó (*el tablero final*). Esto permite destacar al programa como una forma de conseguir que el tablero inicial se transforme en el tablero final, sin que importe el orden en que los pasos individuales necesarios para lograr tal transformación son realizados.
   Creemos que la naturaleza denotacional de un programa requiere mayor aprendizaje y entrenamiento para ser adecuadamente comprendida que la naturaleza operacional. Por esa razón, los comandos de E/S, que enfatizan la segunda y dificultan la conceptualización de la primera, fueron removidos de Gobstones.

   Y esta es la razón fundamental para que Gobstones no tenga E/S: la secuencia Gobstones busca formar pensamiento de naturaleza denotacional, abstracta. Y para eso, la E/S es dañina.

  Continuaremos en futuros posts analizando las características de Gobstones.

4 comentarios:

  1. Concuerdo, aunque estaría bueno justificar, aunque sea con un ejemplo burdo, la afirmación "poner comandos de entrada/salida (E/S) en un programa hace que el programador se focalice casi exclusivamente en la naturaleza operacional del mismo, olvidando o soslayando la naturaleza denotacional." Creo que es lo central al post y lo pasás por al lado...

    ResponderEliminar
    Respuestas
    1. Hola Ricardo,

      He aprendido con Gobstones en la primera materia de la universidad, y luego he enseñado con Gobstones en la escuela secundaria.

      Se me ocurren varios ejemplos de cursos que introducen a la programación centrados fuertemente en E/S:
      a) Cursos que arrancan con PHP para hacer páginas web. Muchas de las ideas que presenta Gobstones están mezcladas con aprender sobre arquitectura de programas web, mañas del lenguaje sobre cómo escribir programas, lenguajes adicionales (HTML y CSS), etc.
      b) Cursos que arrancan con C/C++/Pascal basados en leer el teclado e imprimir texto por la consola.
      c) Interfaces gráficas de escritorio, en, por ejemplo, VisualBasic.
      d) Programación de aplicaciones, en cualquier lenguaje, que persisten datos en alguna base de datos

      En todos los casos detalles espúreos de los lenguajes o las actividades que se plantean con ellos ensucian los contenidos y la secuencia didáctica que un curso introductorio de programación podría plantear si se mantiene alejado de los mismos.

      Luego, todos esos detalles van a poder aprenderse, pero va a existir una base sólida anterior.

      Y ojo, todas los ejemplos que mencioné parecen buenas ideas si uno lo piensa desde el punto de vista de motivar a los alumnos, pero me he encontrado con que en realidad a muchos de ellos los motiva todavía más el poder experimentar en carne propia un curso que presente una interesante base de programación, sobre todo desde el punto de vista de, en determinado punto del curso, estar manipulando conceptos que ellos empiezan a identificar como extremadamente abstractos. Y eso es lo que los termina motivando demasiado, el hecho de ser concientes de haber ganado un poder de abstracción mayor, que con una base pobre no hubiese sido posible.

      La verdad, Gobstones no es lo importante, por eso al principio no digo que aprendi y enseño Gobstones, sino que digo que aprendi CON Gobstones y enseño CON Gobstones. Es sólo una herramienta que tiene la cantidad justa de detalles (porque nunca uno va a poder eliminar todos), lo cual permite que la clase pueda centrarse más por los contenidos abstractos.

      Saludos!

      Eliminar
  2. El tema es que cuando tenés un programa con primitivas de E/S, lo que ves del programa es los puntos de interacción entre el programa y el usuario (tanto para entrada como para salida). Eso hace que sea necesario estar pensando en qué parte del programa (línea de código) se va a producir la interacción, y qué estado intermedio hay en ese momento. De esa manera, el código pensado es operacional y no denotacional.

    Por otra parte, un comando de lectura armado a la ligera (como en la mayoría de los lenguajes) viola el principio de separación entre expresiones y comandos. O sea, es un comando, porque afecta al estado actual, pero podría considerarse una expresión, porque retorna un valor (aunque no siempre el mismo).
    Podría solucionarse si lo incluís como un comando de "lectura-y-asignación"... Entonces podríamos tener "read into x".
    Luego hay que discutir si leemos caracteres o leemos números. Dado que en Gobstones no hay caracteres, podríamos decir que leemos formas atómicas de valores (numerales, constantes de color, dirección y booleanas), pero no sabríamos de antemano el tipo, a menos que lo indiquemos. Entonces quedaría "read Numero into x".

    Un comando de "print" sería más sencillo. Supongamos entonces que tenemos ambos, "read \ into \" y "print(\)".

    ¿Cómo sería un programa para calcular la suma de algunos números? Una versión interactiva "clásica" sería

    ```
    procedure SumaDeValoresInteractiva()
    {
    valorAcumuladoActual := 0
    read Numero into cantidadDeElementos
    repeat (cantidadDeElementos)
    {
    read Numero into valorLeido
    valorAcumuladoActual := valorAcumuladoActual + valorLeido
    }
    print(valorAcumuladoActual)
    }
    ```

    ¿Pero, qué transformación de información describe denotacionalmente este programa? Fijate que al ser un procedimiento, debería solamente modificar el estado... y lo hace, salvo que el estado que modifica informa de estas transformaciones en lugar de explicitarlas. ¿Y si no quiero leer los números por teclado sino sacarlos del tablero? ¿Y si no quiero imprimir el resultado sino usarlo para calcular un promedio o para hacer una acumulación mayor?
    Sería mucho mejor este programa:

    ```
    function sumar(listaDeNumeros)
    {
    valorAcumuladoActual := 0
    foreach elemento in listaDeNumeros
    { valorAcumuladoActual := valorAcumuladoActual + elemento }
    return(valorAcumuladoActual)
    }
    ```

    De esta forma, queda clarísimo que estás procesando la información y que la forma en que conseguís los números es irrelevante.

    Espero que este ejemplo sea del tipo de lo que pediste...

    ResponderEliminar
  3. D*mmit. No me deja indentar código, ni poner no-terminales... Ni tampoco editar el comentario.

    ResponderEliminar