functional programming

Table of Contents

1. functional programming

1.1. Monads

1.1.1. notebook.community Monads

A monad is just a monoid in the category of endofunctors, what’s the problem? -Philip Wadler

1.1.2. Monads and Python – Code happens

In Haskell, the compiler is free to run those functions in either order as there is no data dependency between them. In Python, it is not – the order is specified directly by the code. Haskell requires a data dependency to force ordering (and in fact RealWorld in order to distinguish different invocations of IO)
«Just like the ordering problem with the jupyter notebooks»
https://www.reddit.com/r/Python/comments/qvte5l/monads_and_python/

1.2. Conceptos

1.2.1. https://en.wikipedia.org/wiki/Memoization

In computing, memoization or memoisation is an optimization technique used primarily to speed up computer programs by storing the results of expensive function calls and returning the cached result when the same inputs occur again.

The key is that pure functions can be substituted unambiguously by a lookup table (given X, Y arguments, the output is always Z)

1.2.2. Funciones honestas/transparentes en vez de funciones puras

También se conocen como transparentes: https://en.wikipedia.org/wiki/Referential_transparency
If all functions involved in the expression are pure functions, then the expression is referentially transparent.
An expression is called referentially transparent if it can be replaced with its corresponding value (and vice-versa) without changing the program’s behavior.

Parece que es un mejor término utilizar funciones honestas/transparentes en vez de funciones puras

  • No tiene más entradas ocultas (es decir, más argumentos de los que toma)
  • No tiene más salidas de los que devuelve (es decir, efectos secundarios)

1.2.3. Combinators

Combinator - HaskellWiki
A function or definition with no free variables.
https://en.wikipedia.org/wiki/Combinatory_logic
A combinator is a higher-order function that uses only function application and earlier defined combinators to define a result from its arguments.
loophp/combinator: A curated list of combinators

1.2.4. Idempotent

An operation/function that applied multiple times leaves the system as if was applied only once
Applying it 2 times is the same than applying it once

1.2.4.1. Idempotency key/token

A token that is sent with a function to be executed, to ensure it’s only executed once even if the request is sent multiple times (for example, avoid duplicate orders, or delete the oldest log file even if two requests come along)

1.2.5. Models of computation

1.2.6. Types of evaluation

1.2.6.5. Short-circuit evaluation - Wikipedia
  1. Parallel short-circuit evaluation

    If you have a bunch of subpredicates joined by and/or, then you can apply short-circuit evaluation in parallel, when one of the probes fails/succeeds, then you don’t have to wait for the other probes to finish and evaluate the predicate as false/true

1.2.7. Universal Search Algorithm

The method consists in interleaving the execution of all possible programs on a universal Turing machine, sharing computation time equally among them, until one of the executed programs manages to solve the given inversion problem.

If there exists a program \[p\], of length \[l(p)\], that can solve the problem in time \[time(p)\], then Universal Search will solve the problem in a time \[2^{l(p)+1}time(p)\] at most. This exponential growth of computational cost in the algorithmic complexity \[l(p)\] of the fastest solver makes practical applications of Universal Search problematic: nonetheless, the algorithm has inspired various others, including Hutter Search, the Optimal Ordered Problem Solver (OOPS) and the Gödel Machine.

1.3. Lisp Goodness

1.3.1. https://sites.google.com/site/steveyegge2/tour-de-babel

Two different paradigms

  • C -> von Neummann machine
  • Lisp -> Lisp machine

1.3.3. http://www.paulgraham.com/onlisp.html

We can approach programming in a different way, if we have the right tools.
Why do we plan before implementing? The big danger in plunging right into
a project is the possibility that we will paint ourselves into a corner. If we had
a more flexible language, could this worry be lessened? We do, and it is. The
flexibility of Lisp has spawned a whole new style of programming. In Lisp, you
can do much of your planning as you write the program.

1.3.6. Greenspun’s tenth rule - Wikipedia

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

1.5. Pros and cons of fluent interfaces / method chaining / Pointfree style / Tacit programming

  • FluentInterface
  • Fluent interface - Wikipedia
    • Errors cannot be captured at compile time
      In typed languages, using a constructor requiring all parameters will fail at compilation time while the fluent approach will only be able to generate runtime errors, missing all the type-safety checks of modern compilers. It also contradicts the “fail-fast” approach for error protection.
    • Debugging and error reporting
      Unclear which function call has caused the error, and debuggers are line-oriented, (“step” while debugging is not function oriented for example)
      These issues can be overcome by breaking the statement into multiple lines which preserves readability while allowing the user to set line breakpoints
      PEP 657 : Fine grained locations in tracebacks merged to Python 3.11
    • Logging
      Adding logging into the middle of a chain of fluent calls can be an issue.
      This can be worked around in languages that support extension methods by defining a new extension to wrap the desired logging functionality
    • Subclasses
      Subclasses in strongly typed languages (C++, Java, C#, etc.) often have to override all methods from their superclass that participate in a fluent interface in order to change their return type.
      Languages that are capable of expressing F-bound polymorphism can use it to avoid this difficulty. For example:
      In a dependently typed language, e.g. Scala, methods can also be explicitly defined as always returning this and thus can be defined only once for subclasses to take advantage of the fluent interface:
  • Method chaining - Wikipedia
  • Tacit programming - Wikipedia
  • Fluent Interfaces are Evil
  • Command–query separation - Wikipedia
    It states that every method should either be a command that performs an action, or a query that returns data to the caller, but not both. In other words, asking a question should not change the answer. More formally, methods should return a value only if they are referentially transparent and hence possess no side effects.

1.6. Rewriting / Refactorization

1.7. Avoid shared mutable state

Mutable state is fine, as long as it’s now shared
Shared state is fine, as long as it’s now mutable

This can be converted into a continuum?
Mutable state → change of rate of that state, or variable: information
Shared state → scope of that variable, dependencies
Something like “The Stable Abstractions Principle” in Clean Architecture (pg127)

1.9. Climbing Mount Effect

Cómo programar declarativamente con lenguajes imperativos
Cómo programar funcionalmente con lenguajes imperativos
Some combination of events or interactions corrupts the game’s state in some way. Something changed when it shouldn’t have, or the other way around.

1.9.1. Delta code

The problem is that this code is written as delta-code. It describes state changes, rather than the states themselves. Because there are O(N²) arrows, there will potentially be O(N²) lines of code for dealing with N states.
The real reason to write declarative code is to produce code that is O(N) lines instead. This work does not get harder over time. This is generally not taught in school because neither the students nor their teachers have spent enough time in one continuous codebase.
The goal is to have most code only declare intent. Then you use other code to reach that target, no matter what the prior state was.

1.9.2. Strange Effects

constructor() {
  // Enter state - on
}

onUpdate() {
  // Exit old state - off
  // Enter new state - on
}

dispose() {
  // Exit state - off
}

The trick to fix it is mainly just slicing the code differently, e.g. using a generator:

let effect1 = function* () {
  // Enter state - on
  yield // Wait
  // Exit state - off
}

let effect2 = function* () {
  // Enter state - on
  yield // Wait
  // Exit state - off
}
From To
constructor() Call effect
onUpdate() Cleanup effect
  Call effect
onUpdate() Cleanup effect
  Call effect
onUpdate() Cleanup effect
  Call effect
dispose() Cleanup effect

It doesn’t have fewer calls, just fewer unique parts.

1.9.2.1. effects as a formal type

They are similar to async=/=await and promises. But effects and promises are subtly different. A promise represents ongoing work. An effect is merely a description of work «that you submit to an executor». The difference is:

  • In a promise-based API, fetch(url) => Promise will run a new HTTP request
  • In an effect-based API, fetch(url) => Effect will describe an HTTP request

As a first approximation, you can think of an effect-based API as:

fetch(url) => () => Promise

It won’t actually start until you call it a second time

If a fetch promise fails, there is no way for you to retry using the promise itself. Each is one-use-only. You have to call fetch(url) again to get a fresh one. You need to know the specific type of promise and its arguments.

But if a fetch effect fails, then you can retry the same effect just by passing it back to your effect run-time. You don’t need to know what effect it is.

1.9.2.2. retry combinator

If an error happens, it returns another copy of itself, but with the retry count reduced by 1, until it reaches 0:

let attemptRetry = (effect, n) => function* (i, e) {
  // Make request
  const [value, error] = yield [effect, i, e];
  // Success
  if (value) return [null, value];
  // Retry n times
  if (n > 0) return attemptRetry(effect, n - 1);
  // Abort
  return [null, null, error];
}
1.9.2.3. It’s about the process of making things happen, not the actions themselves.

You can focus purely on defining and declaring intended effects, while letting a run-time figure out how to schedule and execute them. In essence, an Effect is a formalization of 🤙 aka (...) =>. It’s about the process of making things happen, not the actions themselves.

Whether effects are run serially or parallel depends on the use case, just like promises. Whether an effect should actually be disposed of or undone is also contextual: if it represents an active resource, then disposal is necessary to ensure a clean shutdown. But if an effect is part of a transaction, then you should only be rolling it back if the effect chain failed.

But know that even just () => Promise, () => Generator and () => void can solve a whole bunch of issues elegantly.

1.9.3. Disposable

1.12. SMC: The State Machine Compiler

Enter SMC - The State Machine Compiler. Now you put your state diagram in one file using an easy-to-understand language. SMC generates the state pattern classes for you. No more hand-maintained transition matrices. No more widely scattered switch statements. Instead, the state diagram is in one place, coded directly from the picture to the SMC language and is easily maintained.

1.13. jrsinclair   process

1.13.1. What if the team hates my functional code?

  • implicit notifications
const renderNotifications = flow(
  map(addReadableDate),
  map(addIcon),
  map(formatNotification),
  map(listify),
  wrapWithUl
);
  • explicit notifications, unusual syntax
const renderNotifications = (notifications) => pipe(
  notifications,
  map(addReadableDate),
  map(addIcon),
  map(formatNotification),
  map(listify),
  wrapWithUl
);
  • chained methods and functions
const renderNotifications = (notifications) =>
  wrapWithUl(
    notifications
      .map(addReadableDate)
      .map(addIcon)
      .map(formatNotification)
      .map(listify)
  );
  • introduce one variable to avoid chaining functions and methods
const renderNotifications = (notifications) => {
  const renderedItems = notifications
      .map(addReadableDate)
      .map(addIcon)
      .map(formatNotification)
      .map(listify);
  return wrapWithUl(renderedItems);
};
  • separate in multiple assignments, still functional code
const renderNotifications = (notifications) => {
  const withDates = notifications.map(addReadableDate);
  const withIcons = withDates.map(addIcon);
  const formattedItems = withIcons.map(formatNotification);
  const listItems = formatedItems.map(listify);
  return wrapWithUl(listItems);
};

1.13.3. Things I wish someone had explained about functional programming

1.13.3.1. Algebraic Structures: Things I wish someone had explained about functional programming

Words like ‘monoid,’ ‘applicative,’ ’semiring,’ ‘lattice,’ ‘functor,’ or the dreaded ‘monad’? The collective term for these concepts is algebraic structures.

Maybe, Either, and Effect (also known as ‘IO’). We use Maybe, Either, and Effect for different purposes:

  • Maybe helps us deal with null or undefined values;
  • We can use Either to handle errors; and
  • Effect gives us control over side effects.
  • Maybe, Either, and Effect all have a .map() method. Each one also has .ap() and .of() methods. And all three have .chain() too.
  • Algebraic structures are “metaclasses”
    Why do we use classes for anything? Because they abstract common behaviour. That behaviour is shared between a bunch of objects. Algebraic structures, in turn, abstract common patterns shared between a bunch of classes.
1.13.3.2. Type Classes: Things I wish someone had explained about functional programming

Type classes are used to implement algebraic structures. They’re a language feature, rather than a mathematical concept.
Type classes are a way of doing polymorphism

1.13.3.3. Algebraic Data Types: Things I wish someone had explained about functional programming

An algebraic data type is a structured type that’s formed by composing other types. Or, even shorter, it’s a type made of other types.
Product types, sum types
Maybe, Either, and Effect, Reader, Future (also known as Task), and State

1.13.4. https://jrsinclair.com/articles/2020/whats-more-fantastic-than-fantasy-land-static-land/

So, a simple Maybe implementation might look like this: (Type hints)

1.13.5. How to deal with dirty side effects in your pure functional JavaScript

There’s two main ways they do this:

  1. Dependency injection, or as I call it, chucking the problem over the fence; and
    we take any impurities in our code, and shove them into function parameters. Then we can treat them as some other function’s responsibility.
    • ↓ lengthy function signatures
  2. Using an Effect functor, which I think of as extreme procrastination.

1.17. Transforming Programming • Dave Thomas • YOW! 2018 - YouTube   process

From the Pragmatic Programmer, very focused on actual programming and good habits

1.18. TICKLER Haskell in Enterprise: Interview with Rob Harrison   track

My working title is ‘Theory in the Category of Code’. Basically, I find myself on a daily basis having the same discussions over and over about what I consider in our industry to be essentially solved problems or answered questions in industry best practices. My book started out as a way I could share a chapter with my team to avoid having to have the same conversations over and over.

For example, I can’t believe we’re still having to have conversations about the efficacy and efficiency of TDD. For me, on anything but the smallest projects, TDD is a must to avoid the project grinding to a halt as it grows.

1.21. Currying: Partial Functions in Python Inheritance in Functional Programming

Generally, partial application is analogous to inheritance from a general class to a more specialized class.

1.22. No Ghosts! · sunfishcode’s blog

Web Assembly
The ideas in this post aren’t new; they come from papers and blog posts such as Robust Composition, Capabilities: Effects for Free, Parse, don’t validate, the nanoprocess model, and the design choices in the Wasm component model, which itself incorporates ideas from Erlang, OCaml, Rust, COM, and many others.

1.22.1. Ghosts?

By “ghost” here, I mean any situation where resources are referenced by plain data.
And by “plain data” here, I mean strings, integers, or any other data where independently produced copies of the data are interchangeable
For example a path string: while paths are explicit string parameters, the additional information referenced by those paths is not.
IP addresses are another example of plain data that references other resources
Ghosts can also occur within key-value stores, registries, brokers, buses, and many other things where the identifiers are plain data.

1.22.2. The trouble with ghosts

Ghosts are often convenient, in the way that duct tape is convenient. They can quickly connect two things, even in a large system, without extensive changes.

  • Ghosts have four distinct problems as systems scale up in complexity:
    • Ghosts don’t always go to the places we want them to 👻➡😞. Unresolved or bad resolved references
    • Ghosts may go places we don’t want them to 👻➡😲. A process inherits environment variables, a subclass inherits attributes/methods, aliasing of variables prevents full deletion
    • Ghosts may collide with other ghosts 👻➡💥⬅👻. Namespace collision, env variable overriding
    • And sometimes, ghosts come from places they’re not expected to ❓➡👻. Security vulnerabilities, data as code
  • Ghosts complicate static analysis
  • Ghosts complicate debugging
    You can’t isolate components: components work differently when run independently than when they’re run together, or work differently in different environments,
  • Ghosts are often a sign of over-sharing
    Over-sharing happens when a component is given access to resources that it doesn’t need.
    This often happens in namespace-oriented systems because it’s difficult to precisely configure namespaces to be fine-grained
  • Ghosts can contribute to confused deputies
    (Cross-site request forgery is an example)

1.22.3. How to smell a ghost

  • String parameters which don’t represent user data.
    A string that is really a text field, filename, database_id
    Heuristic: “Strings are for humans” 🌟
  • The word “the”.
    Whenever we find ourselves thinking about the filesystem, the network, the process, the host, the OS, or the computer, it often means we’re making assumptions about state that might be shared
    Heuristic: “Components should be hostless” 🌟
  • User identity outside the user interface.
    Heuristic: “Handles are permissions” 🌟

1.23. Functional Programming in JavaScript, Part 1: The Unit

1.24. Why Functional Programming Matters • John Hughes • YOW! 2017 - YouTube

1.24.1. Functional Geometry

1.24.2. Program with whole values, not one word at a time

For example defining operations in vector and matrices and not iterating through the elements of a vector to do an scalar product

1.24.3. Combining forms

1.24.4. Simple laws

Transformations on the code that leave its behavior unchanged
Algebra as a litmus test for good design (finding definitions that satisfy simple laws is indicative of good design)

1.24.5. Function representation

Every value can be represented as a function
Is this getter/setters?

1.24.6. Papers

1.24.6.2. Can Programming Be Liberated from the von Neumann Style? A Functional Style and Its Algebra of Programs

Recommends reading the first part, until IO stuff
The von Neumann computer,

  • their close coupling of semantics to state transitions,
  • their division of programming into a world of expressions and a world of statements (in individual items instead of in whole conceptual units)
  • their inability to effectively use powerful combining forms for building new programs from existing ones (lack good abilities for combination, are too rigid for that), and
  • their lack of useful mathematical properties for reasoning about programs.
  1. FP
1.24.6.5. QuickCheck - Wikipedia
  1. QuickCheck: An Automatic Testing Tool for Haskell

    QuickCheck is a tool for testing Haskell programs automatically. The programmer provides a specification of the program, in the form of properties which functions should satisfy, and QuickCheck then tests that the properties hold in a large number of randomly generated cases. Specifications are expressed in Haskell, using combinators defined in the QuickCheck library. QuickCheck provides combinators to define properties, observe the distribution of test data, and define test data generators.

  2. QuickCheck: Automatic testing of Haskell programs

1.24.7. Consumers vs Producers

The Consumer demands values and it drives the producer to produce them

  • Example 1
    Consumer
    function that uses a finite part of that infinite list (both for iteration or recursion for example)
    Producer
    infinite list
  • Example 2
    Consumer
    Convergence test
    Producer
    Numerical approximations
  • Example 3
    Consumer
    Search strategy
    Producer
    Search space
  • Example 4The Design of a Pretty-printing Library
    Consumer
    Selection for the best layout
    Producer
    Ways to layout a document
  • Example 5QuickCheck - Wikipedia
    Consumer
    QuickCheck search strategy
    Producer
    Space of all possible tests
    • Random search to encounter a failing case, and then systematic search to find the simplest failing case
  • Example 6
    Consumer
    Selection criterion for lowest power
    Producer
    Ways to lay out a parallel prefix circuit

1.25. Charlas

1.25.1. Functional Programming for Pragmatists • Richard Feldman • GOTO 2021 - YouTube

1.25.1.1. Objetivo de la charla

Objetivo → ver las razones concretas y objetivas por las que utiliza la programación funcional en vez de imperativa
Por que normalmente las razones que da la gente suelen ser subjetivas

1.25.1.2. Ámbito rendimiento desarrollo y ecosistema son los puntos de los que va a tratar
1.25.1.3. Scope (Ámbito de la charla)
  1. Definición de programación funcional
    • evitar mutar
    • evitar los efectos secundarios
    1. Cualquier lenguaje puede ser funcional
    2. Lo que añaden los lenguajes funcionales es que sabes que las librerías se han programado en ese estilo
  2. Lenguaje de programación funcional puro

    los lenguajes de programación puros son solo los que permiten funciones puras

  3. Alcance de la charla

    Inmutabilidad, funciones puras, y lenguajes funcionales
    No se consideran Pattern matching, polimorfismo y type checking

  4. Definición de funciones puras
    1. Toman siempre los mismos argumentos
    2. Devuelven siempre el mismo valor
    3. No tienen efectos secundarios
  5. Las funciones puras se pueden sustituir por una tabla

    Lookup table

1.25.1.4. Rendimiento
  1. Cacheo y memoización
    1. Las funciones puras son memoizables por definición
    2. Si una función pura contiene dentro funciones que no son puras entonces deja de serlo
  2. Precomputación

    si estás llamando a una función pura con argumentos que son constantes en tiempo de compilación el compilador puede sustituir esa función por una constante

  3. Concurrencia

    Si estás utilizando la función map y estás llamando a funciones puras entonces puede sustituir esa llamada por un map paralelo, y además es seguro ante problemas de concurrencia

    • Los problemas de concurrencia (data races) tampoco ocurren si no mutas los datos y no hay mutaciones concurrentes
    • Esta no es la única manera de hacerlo y en Rust puedes declarar estas mutaciones de datos y te lo gestiona internamente
  4. Problemas de rendimiento

    las mutaciones en el momento pueden ser más rápidas en lenguajes imperativos
    los efectos gestionados (managed effects) pueden tener también un poco más de coste computacional

  5. Promises vs Tasks
    • https://www.youtube.com/watch?v=3n17wHe5wEw#t=27m38s las promesas en javascript se ejecutan justo después de que las definas entonces eso esté genera efectos secundarios al momento en cambio los defectos gestionados de volverse una tarea pero realmente lo que creas es una cadena de tareas que dependen unas de otras y al final llamas esa
    • Pérdida de rendimiento como tienes que declarar toda la estructura de tu programa tienes que guardar eso de alguna manera en el código si no utilizas promesas en javascript por ejemplo lo puedes declarar de manera imperativa y eso va a ser siempre más rápido que tener que declarar cualquier
1.25.1.6.
1.25.1.7. Tests y refactorización
  • Si tus tests cambian de una vez a otra la ejecución la programación funcional te ayuda con eso porque está evitando los efecto
  • Si no tienes efectos secundarios no te hace falta tener mocking
  • https://www.youtube.com/watch?v=3n17wHe5wEw#t=22m23s
  • Las tareas gestionadas se pueden probar con simulación qué es el equivalente de mocking
  • https://www.youtube.com/watch?v=3n17wHe5wEw#t=24m31s si tus funciones son puras entonces las puedes reordenar y llamarlas en el orden que quieras
    esto también lo implementan las anotaciones de mutaciones como las que tiene rust por ejemplo: más que implementarlo lo alivia porque te dan un warning
  • Incluso si no tiene efectos secundarios cualquier entrada-salida que haga el programa como por ejemplo escribir una base de datos puede tener estos efectos secundarios y no los cogerías con anotaciones
  • https://www.youtube.com/watch?v=3n17wHe5wEw#t=25m36s esto implica al final refactorización más rápida
1.25.1.8. Variables globales
  1. lo malo de las variables globales que se pueden cambiar mutables es que crean dependencia implícitas
  2. https://www.youtube.com/watch?v=3n17wHe5wEw#t=27m44s cualquier variable global mutable que añades a tu código añade automáticamente un argumento de salida y uno de entrada a cada función que tengas
  3. https://www.youtube.com/watch?v=3n17wHe5wEw#t=28m47s los efectos secundarios introducen también dependencias implícitas
  4. https://www.youtube.com/watch?v=3n17wHe5wEw#t=31m00s el hecho de programar con funciones puras así que cuando estés depurando un error tu espacio de búsqueda sea muchísimo menor
  5. https://www.youtube.com/watch?v=3n17wHe5wEw#t=31m50s las dependencias visitas son más ver cosas porque si quieres mutar un objeto en programación funcional pura tienes que crear una función que devuelve ese objeto mutado
1.25.1.9. https://www.youtube.com/watch?v=3n17wHe5wEw#t=32m25s de qué color es tu función es un artículo que dice que si quieres modificar una lista de funciones enlazadas para llamar a una función que devuelve una tarea tienes que modificar todas para que devuelvan a una tarea
1.25.1.10. https://www.youtube.com/watch?v=3n17wHe5wEw#t=35m00s si tú llamas a una función de un paquete pero ese paquete se actualiza y me pasa a tener efectos secundarios te puede romper tu función pura porque si llama una función con efectos secundarios dejar de ser pura
1.25.1.11. Si el ecosistema no está muy centrado en hacer las funciones puras entonces pierdes esto
1.25.1.12. https://www.youtube.com/watch?v=3n17wHe5wEw#t=36m00s de la charla de programación funcional él es un lenguaje de programación funcional basado que luego compila a javascript con su propio sistema de paquetes que te segura que son funcionales
1.25.1.13. Al final el valor de un lenguaje de programación también están sus ecosistemas porque si está programado pensando de manera funcional te aseguras que las funciones son puras

1.26. Erlang

1.26.1. Erlang, the Hidden Gem: Solving Problems at Scale for 30+ Years • Francesco Cesarini • GOTO 2021 - YouTube

1.26.1.1. https://www.youtube.com/watch?v=-m31ag9z4VY#t=5m00s: erlang tiene tres cosas
  1. la primera es el BEAM es la máquina virtual que está optimizada para ejecutarse en múltiples procesadores luego también está
  2. odp qué es el gestionador de todas las abstracciones de concurrencia para que sea más productivo programar y no pases tiempo con los detalles de bajo nivel
  3. El tercer punto es que la semántica de erlang se parece mucho al funcionamiento interno de BEAM por lo cual está muy acoplados y funciona muy bien
1.26.1.2. Elixir es un lenguaje que compila a erlang
  • Fue creado según su desarrollador para abrir erlang a los desarrolladores web a más programadores en general
  • Está más basado para telecomunicaciones erlang
  • https://www.youtube.com/watch?v=-m31ag9z4VY#t=9m45s intentaron desarrollar muchos frameworks para web pero ninguno de ellos les dio el resultado que esperaba → crearon elm
1.26.1.3. Modelo de tareas de erlang
1.26.1.4. Akka (réplica en Java de la máquina virtual de erlang)
1.26.1.5. https://www.youtube.com/watch?v=-m31ag9z4VY#t=20m00s utilizar elixir y erlang para edge computing
1.26.1.6. https://www.youtube.com/watch?v=-m31ag9z4VY#t=21m00s el git de elixir numerical framework y action son librerías que han contribuido para hacer esto posible

1.27. Reactive programming - Wikipedia

Variables and functions as producers & consumers

  • Variables are not mutable, read-only, but can be shared, like producers (shared data)
  • Functions are shared behavior, like consumers

1.27.3. A few programming language features I’d like to see – Neil Madden

constructor theory

Teleo-Reactive programs | Nils Nilsson
approach to programming robots and other systems that is goal-driven but also reacts to unexpected changes in the environment. It doesn’t just keep blindly following a fixed procedure.

The basic idea is that you define a method to achieve some goal as an ordered list of conditions and corresponding actions
If some earlier task was completed but is somehow undone by another action the TRP will notice this the next time it scans through the list and will to that task. On the other hand, if a change in the environment serendipitously causes a higher-up goal to be satisfied already, the system will also notice that and automatically avoid duplicate work.

1.29. https://en.wikipedia.org/wiki/Structured_program_theorem

The structured program theorem, also called the Böhm–Jacopini theorem, is a result in programming language theory. It states that a class of control-flow graphs (historically called flowcharts in this context) can compute any computable function if it combines subprograms in only three specific ways (control structures). These are

  1. Executing one subprogram, and then another subprogram (sequence)
  2. Executing one of two subprograms according to the value of a boolean expression (selection)
  3. Repeatedly executing a subprogram as long as a boolean expression is true (iteration)

1.30. Producer-consumer problem

1.30.1. Rust Is a Scalable Language

Cargo defines a rigid interface for a reusable piece of Rust code. Both producers and consumers must abide by these rules, there is no way around them. As a reward, they get a super-power of working together by working apart.

1.34. Functional programming ideas   idea

1.34.1. Testing as map of test_data and partial equality (Observational Equivalence)

  • Observational Equivalence | PLS Lab
    Two programs in the same programming language are called observationally equivalent whenever they are interchangeable in all observable contexts.

A test is performing a partial equality, func1 is equal to func2 at least when evaluated using the test-data, to the precision required (it doesn’t have to be strict equality when dealing with floating point numbers)

(eq (map func1 test_data) (map func2 test-data))

(map (lambda (x) (eq (func1 x) (func2 x))) test-data)

Test should be parallelizable and independent of ordering if func1 and func2 are both pure functions
func1 is the function you want to test
func2 is a dictionary of concrete cases

A set with a equivalente relation is called a Setoid, and there is a Setoid Type Theory (SeTT)

1.34.1.1. Test space is not homogeneous

Test space is not homogeneous (I cannot remember where I saw that), errors tend to accumulate near edge cases/corner cases
How to reduce test space?

  1. Reduce test space: Is there a test with types?

    Test ensure partial equality between functions, can types reduce the search space?
    With Replicated Data Types as composable types maybe there is a “type of types” and you can optimize the number of tests
    Joe Armstrong says on Haskell you can search on types signatures and also on value properties

  2. Reduce test space: Use hashes of lists of test cases

    From 5 Hobby Projects - Joe Armstrong - EEF17
    Divide your input in lists, hash the input, and hash the output
    You can turn any data structure into a integer by using Gödel Numbering/Mapping

1.34.1.4. Testing is undecidable

https://stackoverflow.com/questions/1132051/is-finding-the-equivalence-of-two-functions-undecidable
https://stackoverflow.com/questions/9906628/equality-of-functions-in-haskell
deciding whether two functions are equal for all possible inputs (without just checking every input value, if that is even possible) is equivalent to solving the Halting problem.

1.34.2. MapReduce is just a lisp progn (DAG) / (Almost) everything is a progn / Everything is a Stream

progn here means: recursive list of functions than can be composed in parallel or sequentially (one after another) → tree-like structure?

Map → execute independent stuff
Reduce → execute dependent stuff
MapReduce
This is just a DAG

pipes en paralelo (DAGs)

1.34.2.3. Interesting language that seems to have been overlooked: the almost-turing-complete Alan language : programming
  1. The Turing-Completeness Problem - Alan Language

    The following snippet of C code succinctly contains the problems of Turing completeness complicating modern development:

    while (node) {
      doSomethingWith(node);
      node = node->next;
    }
    
    1. Automatic Parallelization - It’s not possible to automatically parallelize this code. The number of nodes is not clear to the compiler for it to potentially bunch those calls across threads, and it’s also unclear if the function call mutates the node and adds/removes nodes to process.
    2. Halting Problem - It’s not possible to know if the code will halt, even ignoring the idea that doSomethingWith may always add a new node to the one it is processing, there’s nothing stopping a node from pointing at itself and never halting that way.

    with minor syntax constraints (that we believe the vast majority of developers will not notice, or at least won’t mind) we can be sure that the code you write always halts, and we can parallelize your code for you

1.34.3. Pattern Matching as inverse construction (deconstruction)

immutability […] makes construction reversible. Given an object, you can always disassemble it down to parts that were used in its construction. This deconstruction is done with pattern matching and it reuses constructors as patterns. Constructor arguments, if any, are replaced with variables (or other patterns).

1.34.4. Seamless Scaling

Pasar de manera sencilla de unas cosas a otras que no te lleve a hacer grandes refactorizaciones

1.34.4.1. Programación OOP en Go para pasar de imperativo a oop de manera fluida

Lo que vas haciendo es que si declaras una clase de un argumento con su tipo correspondiente, luego se puede llamar con sintaxis de oop y no tienes que hacer refactor para cambiarlo
Una clase es simplemente una agrupación de funciones cuyo primer método tiene el mismo tipo

1.34.4.2. Variables y funciones como productores y consumidores
  1. Las variables son constantes y sólo lectura, pero son compartidas. Son como los productores(?)
  2. Las funciones son comportamientos compartidos. Son como los consumidores(?)
1.34.4.3. Hay una manera sencilla de pasar de diccionarios a objetos?

Por ejemplo en javascript por defecto es así

1.34.5. Functional Programming in Bash(?)

1.34.5.1. Functional Programming in Bash. simple ways and means to be more… | by joydeep bhattacharjee | Medium
1.34.5.4. Parallel piping
  • Bash represents piping well, and you can parallelize over its elements. However how can you express paralelization more naturally?
    For example redirect the output of a pipe to two different sources and build a DAG

      rg -H "https?://" \
        | sed -E 's%(.+\.org)\:.+?(https?://[^] ]+).+?%\1|\2%g' \
        | parallel --colsep '\|' curl -o /dev/null -s -w '{1}\|{2}\|%{http_code}\\n' '{2}' \
        | tee -a  "link_rot $now.csv"
    
  • Not all Graphs are Trees • Buttondown
You can do this with text:
  o
 / \
o   o
 \ /
  o
But there is not a way to "pass through":
  o
 / \
o---o
 \ /
  o

You need and "identity" node because each step is ... | a | b | c | ... : you cannot connect a with c without passing through b

  o
 / \
o-i-o
 \ /
  o

There are similar constraints when using sets and written language:

  • [2405.10387] Grothendieck’s use of equality
    (a, b) = (c, d) means a = c and b = d because you write a sequence of letters as an ordered sequence. could have (b, a) = (c, d) or any other disordered spatial representation (2D, 3D, …)

1.35. Igualdades

Igualdad entre igualdades: funciona en mi máquina = funciona en tu máquina

1.36. Types

1.36.4. ATS: Why Linear Types are the Future of Systems Programming

1.36.5. Covariance and contravariance (computer science) - Wikipedia

Robustness principle - Postel’s Law in Software: Argument types are contravariant with respect to subtypes (i.e., you can accept a subtype as an argument, but not a supertype), return types are covariant with respect to subtypes (you can return a subtype, but not a supertype)

1.36.6. Church vs Curry Types

Church types (intrinsic types, Haskell) → sound type system
Curry types (extrinsic types, Clojure/Python,Typescript)

Intrinsic types are great because you are guaranteed to have a mathematically-sound safety net at all times. You always have something you can reason about. Such is not guaranteed for extrinsic type checkers. They may not be able to reason about your code at all.

Extrinsic types are useful because you can apply multiple type systems to your code –or even write something that you don’t know how to prove is sound

  1. andrews08.pdf
  2. 13-siek.pdf

1.37. Parser Combinators

1.39. Data Flow Analysis

1.39.1. To convert a data flow diagram into categorical diagrams, swap nodes and arrows

1.40. Haskell GUI Libraries

Author: Julian Lopez Carballal

Created: 2024-09-16 Mon 06:56