Cómo está rehecha: tesis de Maestría

En la búsqueda de un flujo de publicación

Experiencias de la primera manera en como se publicó la investigación:

Aprendizajes posteriores en otras comunidades como Grafoscopio y Miau:

Revisando el trabajo de Knuth, Premio Turing y escritor del sistema de composición tipográfica $\TeX$, se encontró la «programación literaria», un paradigma de programación que, según Wikipedia:

represents a move away from writing computer programs in the manner and order imposed by the computer, and instead enables programmers to develop programs in the order demanded by the logic and flow of their thoughts. Literate programs are written as an uninterrupted exposition of logic in an ordinary human language, much like the text of an essay, in which macros are included to hide abstractions and traditional source code.

Este flujo es una prueba de RuWEB, una implementación de programación literaria hecha con Ruby para poder ejecutar los bloques de código escritos en pads, cuyo funcionamiento puedes consultar en este pad.

¿Qué son las recetas?

Para hablar de recetas, primero hay que retomar cómo funciona un script.

Con un punto de partida en la Wikipedia, un script es un guion de secuencia de comandos que apartir de datos de entrada (inputs) produce datos de salida (outputs).

Un recipe sería una receta que funciona de input como una secuencia de comandos para outputs, la descripción de su funcionamiento e incluso reflexiones en torno a su ejecución.

La cocción de la receta

De manera convencional se dice que una receta está «cruda» cuando RuWEB no ha sido empleado en este input y «cocida» cuando ya se produjeros los outputs.

En esta receta es específico, y como se explicará más adelante, puedes saber que está cruda si puedes leer una # AQUÍ.

Si no pudiste leer la # es porque RuWEB ya procesó esta receta cuyas indicaciones señalas más adelante cocina también este texto.

Para evitar mayores confusiones, puedes leer esta receta en sus dos momentos:

Una metáfora de publicación

Entender la «cocción» de una «receta» «cruda» de manera literal carece de sentido en este texto. Esto se debe a que no se habla aquí de un sistema de publicación, sino de una metáfora de publicación.

Un sistema de publicación es un flujo de producción que puede permitir amplias modificaciones, aunque por lo general están planteadas para tener un flujo específico.

Un sistema de publicación te permite modificar objetos o flujos, pero siempre hay una distancia entre la entidad productora y la entidad producida.

Pero en un contexto digital esa distancia ha sido puesta en el diseño de un flujo que se constituye como «sistema de publicación». El «sistema» para hablar de publicación digital es también una metáfora computacional.

Daniel Ingalls, que junto con Alan Kay son los pioneros en los dos paradigmas más comunes en la computación (leguajes orientados a objetos e interfaces gráficas), nos dice en «Principios de diseño de Smalltalk» (1981):

Objetos: Un lenguaje de computación debe soportar el concepto de «objeto» y proveer una manera uniforme de referirse a los objetos de universo.

Más adelante:

Metáfora uniforme: Un lenguaje debería ser diseñado alrededor de una metáfora poderosa que pueda ser aplicada uniformemente en todas las áre as.

Es decir, Ingalls nos advierte que los «objetos» computacionales son metáforas que nos permiten inteligir lo que una computadora hace pero que no necesariamente está haciendo en la materialidad de sus circuitos interaccionando con una corriente eléctrica.

Incluso los «ceros» y los «unos» que se consideran el lenguaje más bajo para comunicarse con las computadoras es una metáfora, ya que estas operan con una presencia o ausencia de electrones, una danza electromagnética.

Cfr. «Metáforas de la vida cotidiana» (George Lakoff y Mark Johnson, 2009) y «No hay ningún software» (Friedrich Kittler, 2018)

Para publicar los textos de esta investigación se necesita de alguna metáfora que nos permita intelegir su funcionamiento mediante la lectura, pero también la capacidad de escribir este funcionamiento mediante su programación.

¿Sabías que la «receta» para que lo «crudo» por medio de «eventos» sea lo «cocido» es una «metáfora uniforme» de la edición y publicación inspirada en [SAX](http://alturl.com/73aep), una [API](http://alturl.com/9bht7) para [XML](https://es.wikipedia.org/wiki/Extensible_Markup_Language) basada en eventos, la crítica de Derrida a Lévi-Strauss en «La estructura, el signo y el juego en el discurso de las ciencias humanas» y el «Recetario para humanidades desocupadas»?

Pero ¿también sabías que al ser esto la constitución de una metáfora, podrías retomar el código y reescribirlo todo hasta el punto de tener otras metáforas, programas, documentación o publicaciones?

Ahora sí, ¡a cocinar!

La definición de metadatos en Ruby

==TODO== Toda la descripción del código va a ser más expresiva, por el momento está en sus significantes mínimos.

==TODO== Lo primero que se explica son los tipos de datos más comunes en Ruby a partir de la definición de metadatos.

Los primeros metadatos:

META = {
  lang:        'es',
  title:       'El creador y lo creado',
  subtitle:    'Maestría en Filosofía',
  description: 'La propiedad intelectual como supuesto en la producción cultural y filosófica',
  tags:        'investigación, maestría, tesis, nika, zhenya, epub, ebook, publishing, libros, markdown, html, xhtml, mobi, perro, tuerto, propiedad, intelectual, bienes, comunes',
  site:        'https://maestria.perrotuerto.blog',
  contact: {
    email: 'hi@perrotuerto.blog',
    blog:  'https://perrotuerto.blog'
  },
  authors: [{
    name:        'Ramiro',
    lastname:    'Santa Ana Anguiano',
    affiliation: 'UNAM-FFyL'
  }],
  publishers: ['Perro Tuerto'],
  license: {
    name:    'Licencia Editorial Abierta y Libre',
    acronym: 'LEAL',
    url:     'https://gitlab.com/programando-libreros/juridico/licencia-editorial-abierta-y-libre'
  }
}

Manifiesto de la red de pads de este proyecto:

META[:pad_server] = 'https://pad.programando.li/'

META[:pads] = {
  acknowledgements: 'jlCAkyqDST-5HG8tQVPpQA',
  acronyms:         'Fvf4kt6zSDiygthmIN-xSA',
  intro:            'yic1NgzXRfqdEPnjtSHQFw',
  chap1:            '6Me0yOV0TluiGX-xuYnX-g',
  chap2:            'vG2T2khBTW60tAOErUtQMw',
  chap3:            'wMhXyjhBTyW__RVD5oWncA',
  conclusion:       'WnIO3M_nSYODaLiGGyd-Vw',
  glossary:         'RuYyNvjeQ0WidMee7unIVQ',
  todo_list:        'jQwG6o40Sm20EGsncIS23w',
  bib:              '6LiuDuMbRiyvLYu2Ix5w3A',
  report1_es:       'XE1d-Mb1RiK5hfwOWrnKkQ',
  report1_en:       'bHS6ZMrCSti9hEttm-28wA',
  report2_es:       '_CrMDL2FS6Ouu3umzgDOkQ',
  report2_en:       'KAdEIUXpT-q6gsIl1HxogQ',
  essay_es:         'G_0bDEq-Sh-Xhzs2BrVqIA',
  essay_en:         'jcRrTwa4S_2cuOl7uQ8yuw',
  index:            'NEfYglcaTLOFF0xdg9D4LA'
}

Orden de lectura de la publicación:

META[:spine] = [
  'acknowledgements',
  'acronyms',
  'intro',
  'chap1',
  'chap2',
  'chap3',
  'conclusion',
  'glossary',
  'todo_list'
]

Declaración de títulos

META[:titles] = {
  acknowledgements: 'Agradecimientos',
  acronyms:         'Lista de siglas y acrónimos',
  intro:            'Introducción. Un tema filosófico desatendido',
  chap1:            'Teorías de la propiedad intelectual',
  chap2:            'Críticas de los bienes comunes',
  chap3:            'Puntos de encuentro',
  conclusion:       'Conclusión. YYY',
  glossary:         'Glosario',
  todo_list:        'Tareas pendientes',
  bib:              'Bibliografía',
  report1_es:       'Cómo está hecha: tesis de Maestría',
  report1_en:       'How It Is Made: Master Research Thesis',
  report2_es:       'Cómo está rehecha: tesis de Maestría',
  report2_en:       'How It Is Remade: Master Research Thesis',
  essay_es:         'Metafísicas y tecnologías de la creación',
  essay_en:         'Metaphysics and technologies of creation'
}

Declaración de enlaces relevantes:

META[:research] = {
  html:  'research/creador_creado.html',
  icml:  'research/creador_creado.xml',
  pdf:  'research/creador_creado.pdf',
  epub: 'research/creador_creado.epub',
  docx: 'research/creador_creado.docx',
  mobi: 'research/creador_creado.mobi'
}

META[:bib] = {
  db:       'bib/bibliografia.bib',
  query:    'bib/bibliografia.html',
  download: 'https://gitlab.com/NikaZhenya/bibliografia/-/tree/master'
}

META[:repo] = {
  oxacab: 'https://0xacab.org/NikaZhenya/maestria-investigacion',
  gitlab: 'https://gitlab.com/NikaZhenya/maestria-investigacion'
}

META[:dl] = {
  zip: 'https://gitlab.com/NikaZhenya/maestria-investigacion/-/archive/ed2/maestria-investigacion-master.zip',
  tar: 'https://gitlab.com/NikaZhenya/maestria-investigacion/-/archive/ed2/maestria-investigacion-master.tar',
  gz:  'https://gitlab.com/NikaZhenya/maestria-investigacion/-/archive/ed2/maestria-investigacion-master.tar.gz',
  bz2: 'https://gitlab.com/NikaZhenya/maestria-investigacion/-/archive/ed2/maestria-investigacion-master.tar.bz2'
}

META[:misc] = {
  essay_es:   'misc/ensayo.html',
  essay_en:   'misc/essay.html',
  report1_es: 'misc/reporte1.html',
  report1_en: 'misc/report1.html',
  report2_es: 'misc/reporte2.html',
  report2_en: 'misc/report2.html'
}

Los primeros métodos en Ruby

==TODO== De la descripción de datos se pasa a la explicación de la implementación de método en Ruby.

Método para obtener una marca temporal en segundos

def puts_timestamp
  Time.now.to_i.to_s
end

Método para obtener la fecha

def puts_date
  t = Time.now
  m = ['enero', 'febrero', 'marzo', 'abril', 'mayo', 'junio', 'julio', 'agosto', 'septiembre', 'octubre', 'noviembre', 'diciembre']
  "#{t.day} de #{m[t.mon - 1]} del #{t.year}"
end

Método para eliminar los metadatos de escritura de los pads:

def remove_yaml_from(pad)
  pad.sub(/^---\n+(.|\n)+?---(\n+|$)/, '')
end

La eliminación de metadatos de escritura es para permitir asignar valores distintos para su publicación. Sin embargo, la eliminación no es necesaria.

La obtención del contenido de los pads

Método para extraer el contenido de un pad

require 'open-uri'
def obtain_content_from(pad_id)
  url = "#{META[:pad_server]}#{pad_id}/download"
  URI.parse(url).open.read.strip
end

De los id de los pads a su contenido por medio de una iteración. Los crudos se guardan para archivo

raw_data = {}
data     = {}
META[:pads].each do |pad_name, pad_id|
  raw_data[pad_name] = obtain_content_from(pad_id)
  data[pad_name] = remove_yaml_from(raw_data[pad_name])
end

El contenido para la publicación

Para las publicaciones se tienen que unir los pads declarados en el orden de lectura y eliminar los contenidos sueltos

publication_in_md = ''
META[:spine].each do |pad_name|
  pad_name = pad_name.to_sym
  publication_in_md += "\n\n#{data[pad_name]}"
  data.delete(pad_name)
end
data[:creador_creado] = publication_in_md.strip

Unas pruebas de marcado

Con los contenidos listos para su procesamiento, se realizan algunas pruebas al respecto.

Asignación del símbolo que servirá de separador, a modo de punto entre la cadena de caracteres arbitrarios que conforman un texto.

E = "#"

Podría ser cualquier símbolo, se escoge # por su amplio uso como #etiquetador. La diferencia en su uso como separador es que se empleará para determinar un «evento»: el posicionamiento en una parte de la cadena que permite procesar caracteres a la izquierda y a la derecha. Para ello se requiere un método que nos permita capturar caracteres a petición de un lado y del otro que, de coincidir, permita una retrollamada para cambiar lo que sea y como sea del texto capturado.

def cut(pad, a:, b:, callback:)
  regex = /(?<prefix>.{0,1})(?<a>#{a})#{E}(?<b>#{b})/
  pad.gsub(regex).each_with_index do |string, index|
    if string[0].ord != 96
      els = string.match(regex)
      mod  = send(callback.to_sym, els[:a], els[:b], index)
      "#{els[:prefix]}#{mod}"
    else string end
  end
end

El texto como cadena de caracteres se simboliza como a. El separador hace un corte que da lugar a a ∧ b. La separación solo es posible si a y b coinciden con la regla de captura determinada por el usuario. Esto es un «evento» que desata una retrollamada para procesar la captura de a ∧ b como argumentos independientes.

Además, esta retrollamada cuenta con un tercer argumento i, la cual indica el número de índice de cada coincidencia de la condición a ∧ b. Esto es útil para establecer relaciones entre coincidencias que no están presentes en el texto crudo.

Una prueba que convierte cualquier marca

v#Versalita

en versalita como

<span class="smallcaps" style="text-transform:lowercase">Versalita</span>

def to_smallcaps(a, b, i)
  "<span class=\"smallcaps\" style=\"text-transform:lowercase\">#{b}</span>"
end

Como la retrollamada solo se activa si acontece a ∧ b, las retrollamadas siempre cuentan con tres argumentos para a, b y el índice: a, b, i. Para el caso de las versalitas, las regla de corte de a siempre coincidirá con v, mientras que la condición de b siempre coincidirá con cualquier letra, número, tipo de guion o un símbolo de evento.

Entonces, a nos permite tener un significado y, con ello, una conversión a versalitas. Cada que haya coincidencia, con v se puede determinar que su modificación será una versalita.

Ojo que a = v es arbitrario, bien pudo haer sido a = sc para sc#Versalita o a = blablablá para blablablá#Versalita y así obtener una versalita.

Otra prueba de marcado será con cualquier #frances que vaya de

Una línea #frances

a

<p class="french">Una línea</p>

def to_french_paragraph(a, b, i)
  a = a.strip
  b = b.gsub('frances', '')
  "<p class=\"french\">#{a}</p>#{b}"
end

En las reglas de corte se indicará que del lado izquierdo se capturará cualquier caracter hasta el inicio de una línea y del lado derecho, además de frances, se capturará cualquier espacio o tabulador antes del fin de la línea.

En este siguiente prueba se usará el argumento i para añadir numeración a las figuras a partir de la marca #fig.

def numerate_figure(a, b, i)
  figure = "Figura #{i + 1}. "
  "#{a}#{figure}"
end

La condición de a será a = ![, que es la sintaxis para una imagen en Markdown, mientras que la regla de corte b será b = fig. El lado b le da significado al corte, pero en el procesamiento se utiliza a, presente en el texto, e i, generada por esta receta.

Como última prueba, se declara una retrollamada para la numeración de los tres capítulos donde a #CHAP más cualquier dígito se le añadirá una numeración alfabética de la forma A, B, C.

def numerate_chapter(a, b, i)
  abc    = ['A', 'B', 'C']
  number = b.gsub(/\D/, '').to_i - 1
  letter = "#{abc[number]}. "
  "#{a}#{letter}#{E}#{b}"
end

La regla de corte a será a = inicio de línea + definición de encabezado1. La condición b será b = CHAP + dígitos. Con b se obtiene un número de índice para un conjunto A, B, C, que permite sustituir el índice por uno de estos caracteres.

Ahora es momento de implementar los métodos, se hará según su orden de definición que en ciertas circunstancias puede ser relevante:

data.each do |pad_name, pad_content|
  data[pad_name] = cut(data[pad_name], a: 'v', b: /[#{E}:alpha:-_]+/, callback: 'to_smallcaps')
  data[pad_name] = cut(data[pad_name], a: /^.+/, b: /frances[\s]*$/, callback: 'to_french_paragraph')
  data[pad_name] = cut(data[pad_name], a: /^\!\[/, b: /fig/, callback: 'numerate_figure')
  data[pad_name] = cut(data[pad_name], a: /^\#\s+/, b: /CHAP\d+/, callback: 'numerate_chapter')
end

Esta gestión de marcado permite usar otros métodos como argumentos y no solo cadena de caracteres o expresiones regulares. Cabe la posibilidad de hacer retrollamadas constantes hasta que cierta condición se cumpla, por lo que es posible la modificación de a ∧ b como gramática libre de contexto.

Unas pruebas con constantes

Según Jesus Castellos, las constantes en Ruby son:

A constant is a type of variable which always starts with a capital letter. They can only be defined outside of methods, unless you use metaprogramming. Constants are used for values that aren’t supposed to change, but Ruby doesn’t prevent you from changing them.

Las constantes nos permiten tener una lógica de datos distinta a la lógica de categorización en un texto, ya que nos permite establecer equivalencias.

Nuestras primeras constantes establecen equivalencias con los metadatos definidos en esta receta:

ACKNOWLEDGEMENTS = META[:titles][:acknowledgements]
ACRONYMS         = META[:titles][:acronyms]
BIB              = META[:titles][:bib]
BIB_DB           = META[:bib][:db]
BIB_DOWNLOAD     = META[:bib][:download]
BIB_QUERY        = META[:bib][:query]
BLOG             = META[:contact][:blog]
BZ2              = META[:dl][:bz2]
CHAP1            = META[:titles][:chap1]
CHAP2            = META[:titles][:chap2]
CHAP3            = META[:titles][:chap3]
CONCLUSION       = META[:titles][:conclusion]
DESCRIPTION      = META[:description]
DOCX             = META[:research][:docx]
EMAIL            = META[:contact][:email]
EPUB             = META[:research][:epub]
ESSAY_EN         = META[:titles][:essay_en]
ESSAY_EN_URL     = META[:misc][:essay_en]
ESSAY_ES         = META[:titles][:essay_es]
ESSAY_ES_URL     = META[:misc][:essay_es]
GITLAB           = META[:repo][:gitlab]
GLOSSARY         = META[:titles][:glossary]
GZ               = META[:dl][:gz]
INTRO            = META[:titles][:intro]
LANG             = META[:lang]
LICENSE          = META[:license][:acronym]
LICENSE_NAME     = META[:license][:name]
LICENSE_URL      = META[:license][:url]
MOBI             = META[:research][:mobi]
OXACAB           = META[:repo][:oxacab]
PDF              = META[:research][:pdf]
R1_EN            = META[:misc][:report1_en]
R1_ES            = META[:misc][:report1_es]
R2_EN            = META[:misc][:report2_en]
R2_ES            = META[:misc][:report2_es]
REPORT1_EN       = META[:titles][:report1_en]
REPORT1_ES       = META[:titles][:report1_es]
REPORT2_EN       = META[:titles][:report2_en]
REPORT2_ES       = META[:titles][:report2_es]
SUBTITLE         = META[:subtitle]
TAGS             = META[:tags]
TAR              = META[:dl][:tar]
TITLE            = META[:title]
TODO_LIST        = META[:titles][:todo_list]
WEB              = META[:research][:html]
XML              = META[:research][:icml]
ZIP              = META[:dl][:zip]

Las constantes pueden tener cualquier tipo de dato, incluyendo aquellos que son resultado de un método. Por eso es posible tener constantes para tener la fecha y la marca temporal de cada vez que esta receta se mande a cocer:

DATE = puts_date
T    = puts_timestamp

Método que coloca el valor de una constante a partir de una cadena de texto

def puts_value_from(string)
  if string[0].ord != 96
    pre = string[0]
    key = string[2..-1]
    val = Object.const_defined?(key) ? Object.const_get(key) : nil
    val ? pre + val : string
  else string end
end

Método que reemplaza contenido con constantes en un pad

def replace_content_of(pad)
  pad = META[:pads].has_value?(pad) ? obtain_content_from(pad) : pad
  pad.gsub(/.#[A-Z0-9_]+/) { |match| puts_value_from(match) }
end

Una vez declarados los métodos, se implementa el intercambio entre las constantes y sus valores:

data.each do |pad_name, pad_content|
  data[pad_name] = replace_content_of(data[pad_name])
end

La sintaxis de las constantes a reemplazar empiezan con #, por ejemplo, #TITLE. El reemplazo de la constante por su valor solo es efectivo si esta no está escrita en una línea de código. Por eso es que #TITLE es la declaración de una constante en una línea de código que no es reemplazada por su valor.

Pruebas de reemplazos:

La automatización con Pandoc

Una vez terminada la edición, nos ocupamos ahora del proceso de publicación definiendo el método que implementa Pandoc.

require 'pandoc-ruby'
def convert(from: false, to: false, prefix: '', suffix: '', arguments: '--eol=native')
  markdown    = from
  file_format = to
  from_md     = "#{prefix}#{markdown}#{suffix}"
  with_args   = arguments
  PandocRuby.new(from_md, with_args).send("to_#{file_format}")
end

El método convert convertirá del contenido en MD a diversos formatos estipulados a continuación. De manera opcional se puede indicar un prefijo de datos para añadir texto antes del texto del contenido en Markdown. También es posible añadir sufijo de datos, además de argumentos para procesos de publicación específicos.

La multipublicación en Git

Al fin algunas metáforas familiares…

Método para generación de directorios (carpetas)

require 'fileutils'
def make_dirs(*names)
  names.each do |name|
    FileUtils.rm_r(name) if File.directory?(name)
    Dir.mkdir(name)
  end
end

Generación de directorios

make_dirs('public', 'public/bib', 'public/research', 'public/misc', 'public/src', 'public/src/raw', 'public/src/cooked')

Guardado y publicación del index

index_in_markdown = data[:index]
index_content = convert(from: index_in_markdown, to: 'html')
File.write("public/index.html", index_content)

Publicación de ficheros (archivos) MD crudos

raw_data.each do |pad_name, pad_content|
  file = "public/src/raw/#{pad_name}.md"
  File.write(file, pad_content)
end

Publicación de ficheros (archivos) MD cocidos

data.each do |pad_name, pad_content|
  file = "public/src/cooked/#{pad_name}.md"
  File.write(file, pad_content)
end

Generación de metadatos de publicación

def puts_meta_template(title)
  template = <<-'EOS'
---
lang: es
pagetitle: "#title"
---
  EOS
  template.gsub('#title', title)
end

Publicación de anexos

META[:misc].each do |pad_name, file_path|
  metadata = puts_meta_template(META[:titles][pad_name])
  markdown = data[pad_name]
  file     = "public/#{file_path}"
  content  = convert(from: markdown, to: 'html', prefix: metadata, arguments: '--standalone')
  File.write(file, content)
end

Publicación de la investigación en múltiples formatos

META[:research].each do |format, file_path|
  metadata = puts_meta_template(META[:title])
  markdown = data[:creador_creado]
  file     = "public/#{file_path}"
  case format
  when :mobi
    epub = File.basename(EPUB)
    Dir.chdir('public/research')
    system("kindlegen #{epub}")
  else
    content = convert(from: markdown, to: format.to_s, prefix: metadata, arguments: '--standalone')
    File.write(file, content)
  end
end

La producción del formato propietario es lo último que hace, porque así fue definido en los metadatos, por eso es posible cambiar de directorio, una cuestión que puede resultar problemática al momento de hacer operaciones con directorios y ficheros.

Aquí termina cocción de esta receta con RuWEB.

==TODO== Expicación de cómo mandar a cocer.

Si se realizó en una pipa que trabaja en el repositorio Git de esta investigación, en este momento toda la información ha sido guardada y los datos ya se encuentran disponibles públicamente en el texto cocido de esta receta.

El resultado

Con todas estas etapas de edición se obtuvieron 37 ficheros, 7 publicaciones, 4 formatos de salida, 2 reportes, 1 ensayo, 1 sitio web y 1 libro en diversos formatos, para impresión o lectura en digital, a partir de 1 formato de entrada y usando procesos automatizados con software libre.

Nosotros no modificamos ni creamos ningúna carpeta, archivo o libro, pero sin duda produjimos este «árbol» de directorios a partir de esta receta:

Árbol de directorios generado automáticamente.

El proceso de producción escrito en Markdown y programado en Ruby toma entre ~30–90 s en producirse, y si se uso Git, también de autoguardarse.

El flujo se puede inspeccionar en diferentes temporalidades:

No se requirieron los programas necesarios para publicar ni se empleó la terminal. El proceso puede hacerse online pero también puede adaptarse para trabajar en una red local de una comunidad, escuela o universidad.

Una maquinaria de publicación que no es una caja negra, sino en pos de la cultura, el software y la edición libres.

Este reporte describe cómo está rehecha la publicación de la investigación. Pero al mismo tiempo, cuando el reporte se ejecuta, hace lo que describe: publica la investigación.

En un solo documento está el programa y su documentación. Pero quizá no solo eso… En My Mother Was a Computer (2005), Katherine Hayles nos indica:

I claim that theories about speech, writing, and code as represented by these sources are fraught with presuppositions, premises, and implications, in effect constituting what, for lack of a better term, I call “worldviews.” The worldviews of speech, writing, and code are inextricably entwined with the technological conditions enveloping the theorists that developed these ideas, the philosophical, linguistic, and scientific traditions in which they claimed lineage, and the purposes toward which they wanted their work to contribute.

En está maquinaria de escritura y de publicación, ¿se produjo un solo texto donde están los tres “worldviews” del discurso, escritura y código? ¿Es posible escribir de otra manera donde no esté tan presente la «división» del Markdown en bloques de código ejecutables y bloques no ejecutables?

¿Es posible que todos los bloques, sin importar su sintaxis, puedan ser ejecutables?

Tareas pendientes