Custom Blocks: Guía de estilo

A lo largo de los años, el equipo de Blockly y Blockly Games aprendió muchas lecciones que se pueden aplicar a aquellos que desarrollan nuevos bloques. A continuación, se muestra un conjunto de errores que cometimos o que suelen ser cometidos por otras personas.

Estas son lecciones generales que aprendimos con el estilo visual de Blockly y es posible que no se apliquen a todos los casos de uso o diseños. Existen otras soluciones. Tampoco es una lista exhaustiva de los problemas que pueden encontrar los usuarios y cómo evitarlos. Cada caso es un poco diferente y tiene sus ventajas y desventajas.

1. Condicionales frente a bucles

Los bloques más difíciles para los usuarios nuevos son los condicionales y los bucles. Muchos entornos basados en bloques agrupan ambos bloques en la misma categoría "Controles", y ambos tienen la misma forma y el mismo color. Esto suele provocar frustración, ya que los nuevos usuarios confunden los dos bloques. Blockly recomienda mover los condicionales y los bucles a categorías independientes de "Logic" y "Loops", cada una con un color diferente. Esto deja en claro que estas son ideas distintas que se comportan de manera diferente, a pesar de tener formas similares.

Recomendación: Mantén los condicionales y los bucles separados.

2. Listas basadas en uno

Los programadores principiantes reaccionan mal cuando encuentran listas basadas en cero por primera vez. Como resultado, Blockly sigue el ejemplo de Lua y Lambda Moo con una indexación de listas y cadenas basada en uno.

Para usos más avanzados de Blockly, se admiten listas basadas en cero para facilitar la transición a texto. Para los públicos más jóvenes o novatos, se recomienda la indexación basada en una sola.

Recomendación: Uno es el primer número.

3. Entradas del usuario

Existen tres maneras de obtener un parámetro del usuario. Un menú desplegable es la más restrictiva y es útil para instructivos y ejercicios simples. Un campo de entrada permite una mayor libertad y es bueno para actividades más creativas. Una entrada de bloque de valores (por lo general, con un bloque sombra) ofrece la oportunidad de calcular un valor (p.ej., un generador aleatorio) en lugar de ser solo un valor estático.

Recomendación: Elige un método de entrada adecuado para los usuarios.

4. Imágenes de bloqueo en vivo

La documentación de los bloques debe incluir imágenes de los bloques a los que se refiere. Tomar capturas de pantalla es fácil. Sin embargo, si hay 50 de esas imágenes y la aplicación se traduce a 50 idiomas, de repente una mantendrá 2,500 imágenes estáticas. Luego, el esquema de colores cambia y se deben actualizar 2,500 imágenes.

Para sacarnos de esta pesadilla por el mantenimiento, Blockly Games reemplazó todas las capturas de pantalla con instancias de Blockly ejecutándose en modo de solo lectura. El resultado es idéntico al de una foto, pero se garantiza que esté actualizado. El modo de solo lectura permitió la internacionalización.

Recomendación: Si admites más de un idioma, usa el modo de solo lectura.

5. Tu otro a la izquierda

Los comentarios de niños en EE.UU. (aunque curiosamente no de otras naciones) revelaron una gran confusión entre la izquierda y la derecha. Esto se resolvió con la adición de flechas. Si la dirección es relativa (por ejemplo, a un avatar), el estilo de la flecha es importante. Una flecha recta → o una flecha de giro ↱ son confusas cuando el avatar está apuntando en la dirección opuesta. Lo más útil es una flecha circular ⟳, incluso en los casos en los que el ángulo invertido es menor de lo que indica la flecha.

Recomendación: Complementa el texto con íconos Unicode siempre que sea posible.

6. Bloques de alto nivel

Siempre que sea posible, se debe adoptar un enfoque de mayor nivel, incluso si reduce la flexibilidad o el rendimiento de ejecución. Considera esta expresión de Apps Script:

SpreadsheetApp.getActiveSheet().getDataRange().getValues()

En una asignación 1:1 que conserve todas las capacidades posibles, la expresión anterior se compilaría con cuatro bloques. Sin embargo, Blockly busca un nivel más alto y proporciona un bloque que encapsule toda la expresión. El objetivo es realizar optimizaciones para el caso del 95%, incluso si dificulta el 5% restante. Blockly no está diseñado para reemplazar los idiomas basados en texto, sino que ayuda a los usuarios a superar la curva de aprendizaje inicial para que puedan usar idiomas basados en texto.

Recomendación: No conviertas a ciegas toda tu API en bloques.

7. Valores de retorno opcionales

Muchas funciones de la programación basada en texto realizan una acción y, luego, muestran un valor. Este valor que se muestra puede usarse o no. Un ejemplo es la función pop() de una pila. Es posible que se llame a Pop para obtener y quitar el último elemento, o que se lo llame solo para quitar el último elemento en el que se ignora el valor que se muestra.

var last = stack.pop();  // Get and remove last element.
stack.pop();  // Just remove last element.

Por lo general, los idiomas basados en bloques no son buenos para ignorar un valor que se muestra. Un bloque de valor debe conectarse con algo que acepte el valor. Existen varias estrategias para abordar este problema.

a) Evita el problema. La mayoría de los lenguajes basados en bloques diseñan el lenguaje para evitar estos casos. Por ejemplo, Scratch no tiene ningún bloque que tenga efectos secundarios y un valor que se muestre.

b) Proporciona dos bloques. Si el espacio en la caja de herramientas no es un problema, una solución simple es proporcionar dos de cada uno de estos bloques, uno con un valor y otro sin un valor que se muestre. La desventaja es que esto puede generar una caja de herramientas confusa con muchos bloques casi idénticos.

c) Mutar un bloque. Usa un menú desplegable, una casilla de verificación o algún otro control que permita al usuario elegir si se muestra un valor o no. Luego, el bloque cambia de forma según sus opciones. Un ejemplo de esto se puede ver en el bloque de acceso a listas de Blockly.

d) Come el valor. La primera versión de App Inventor creó un bloque de canalización especial que consumía cualquier valor conectado. Los usuarios no comprendieron el concepto, y la segunda versión de App Inventor quitó el bloque de canalización y, en su lugar, recomendó que los usuarios asignaran el valor a una variable desechable.

Recomendación: Cada estrategia tiene ventajas y desventajas. Elige la que sea más adecuada para los usuarios.

8. Bloques de crecimiento

Es posible que ciertos bloques requieran un número variable de entradas. Algunos ejemplos son un bloque de adición que suma un conjunto arbitrario de números, un bloque if/elseif/else con un conjunto arbitrario de cláusulas elseif o un constructor de lista con un número arbitrario de elementos inicializados. Hay varias estrategias, cada una con sus ventajas y desventajas.

a) El enfoque más simple es hacer que el usuario componga el bloque con bloques más pequeños. Un ejemplo sería sumar tres números, mediante la anidación de dos bloques de suma de dos números. Otro ejemplo sería solo proporcionar bloques "if/else" y hacer que el usuario los anide para crear condiciones elseif.

La ventaja de este enfoque es su simplicidad inicial (para el usuario y el desarrollador). La desventaja es que en los casos en que hay una gran cantidad de anidamiento, el código se vuelve muy engorroso y difícil de leer y mantener para el usuario.

b) Una alternativa es expandir el bloque de forma dinámica, de modo que siempre haya una entrada libre al final. Del mismo modo, el bloque borra la última entrada si hay dos entradas libres al final. Este es el enfoque que usó la primera versión de App Inventor.

Los usuarios de App Inventor no les gustaron los bloques que crecieron automáticamente por dos razones. Primero, siempre hubo una entrada gratuita y el programa nunca estuvo "completo". En segundo lugar, insertar un elemento en el medio de la pila era frustrante, ya que implicaba desconectar todos los elementos debajo de la edición y volver a conectarlos. Dicho esto, si el orden no es importante y los usuarios pueden sentirse cómodos con los vacíos en su programa, esta es una opción muy conveniente.

c) Para resolver el problema del agujero, algunos desarrolladores agregan botones +/- a los bloques que agregan o quitan entradas de forma manual. Abre Roberta usa dos botones para agregar o quitar entradas de la parte inferior. Otros desarrolladores agregan dos botones en cada fila para que se pueda adaptar la inserción y la eliminación del centro de la pila. Otros agregan dos botones de arriba y abajo en cada fila para que se pueda reordenar la pila.

Esta estrategia es un espectro de opciones que va desde solo dos botones por bloque hasta cuatro por fila. En un extremo, existe el peligro de que los usuarios no puedan realizar las acciones que necesitan. En el otro, la IU está tan llena de botones que parece el puente de la nave espacial Enterprise.

d) El enfoque más flexible es agregar una burbuja de mutador al bloque. Esto se representa como un solo botón que abre un diálogo de configuración para ese bloque. Los elementos se pueden agregar, borrar o reorganizar cuando quieras.

La desventaja de este enfoque es que los mutadores no son intuitivos para los usuarios principiantes. Para ingresar mutadores, se requiere algún tipo de instrucción. Las aplicaciones basadas en Blocks que se orientan a niños pequeños no deben usar mutadores. Si bien una vez aprendidas, son invaluables para los power users.

Recomendación: Cada estrategia tiene ventajas y desventajas. Elige la que sea más adecuada para los usuarios.

9. Generación de código limpio

Los usuarios avanzados de Blockly deberían poder ver el código generado (JavaScript, Python, PHP, Lua, Dart, etc.) y reconocer inmediatamente el programa que escribieron. Esto significa que se deben realizar más esfuerzos para que este código generado por máquina sea legible. Los paréntesis superfluos, las variables numéricas, los espacios en blanco triturados y las plantillas de código detallado obstaculizan la producción de código elegante. El código generado debe incluir comentarios y debe cumplir con las guías de estilo de Google.

Recomendación: Siéntete orgulloso de tu código generado. Muéstraselo al usuario.

10. Dependencia del idioma

Un efecto secundario del deseo de tener un código limpio es que el comportamiento de Blockly se define en gran medida en términos de cómo se comporta el lenguaje de compilación cruzada. El lenguaje de salida más común es JavaScript, pero, si Blockly realizara una compilación cruzada en un lenguaje diferente, no se deberían realizar intentos injustificados de preservar el comportamiento exacto en ambos lenguajes. Por ejemplo, en JavaScript, una cadena vacía es falsa, mientras que en Lua es verdadera. Definir un solo patrón de comportamiento para que se ejecute el código de Blockly independientemente del lenguaje de destino generaría un código insostenible que pareciera surgido del compilador de GWT.

Recomendación: Blockly no es un lenguaje. Permite que el idioma existente afecte el comportamiento.