Descobrindo Mônadas com Ruby

Table of Contents

1 Motivação

Usamos abstrações para falar sobre as coisas. Gostamos de dar nomes a padrões de comportamento.

A matemática tem muitas definições úteis.

'The purpose of abstraction is not to be vague, but to create a new semantic level in which one can be absolutely precise' - Edsger Dijkstra

2 Teoria das Categorias

Teoria das categorias é um campo da matemática Frequentemente usada para abordar conceitos de uma forma ainda mais abstrata.

category_theory_for_programmers.jpg

3 Duct Tape

  • sealing ducts
  • fixing CO2 scrubbers on board Apollo 13
  • wart treatment
  • fixing Apple’s iPhone 4 dropped call issue
  • making a prom dress
  • building a suspension bridge

4 Caixinha

half.png half_ouch.png

5 Problemas

Alguns problemas tradicionalmente solucionados de forma a perder a pureza das funções:

  • Parcialidade, Exceções
  • Não determinismo
  • Efeitos colaterais
  • Read-only (env), Write-only (log)
  • Continuações
  • I/O

6 Ruby

Eu tenho:

  • Estado global
  • Exceptions

Eu quero:

  • Idempotência
  • Testar tudo o que uma função faz

7 Exceptions

class Object
  def try(method)
    self.send(method)
  end
end

class NilClass
  def try(_)
    nil
  end
end

@person && @person.children.any? && @person.children.first.name
@person.try(:children).try(:first).try(:name)

Algumas linguagens estaticamente tipadas tratam valores nullable como tipos a parte.

8 Maybe

def safe_div(a, b)
  b == 0 ? nil : a/b
end

# (64 / 4) / (16 / 2)
r1 = safe_div(64, 4)
r2 = safe_div(16, 2)
r1.nil? || r2.nil? ? nil : safe_div(r1, r2)
require 'dry/monads/maybe'
require 'dry/monads/do'

extend Dry::Monads::Maybe::Mixin
include Dry::Monads::Do.for(:call)

def safe_div(a, b)
  Maybe(b == 0 ? nil : a / b)
end

safe_div(64, 4).bind do |a|
  safe_div(16, 2).bind do |b|
    puts safe_div(a, b)
  end
end

def call
  a = yield safe_div(64, 4)
  b = yield safe_div(16, 2)
  safe_div(a, b)
end

call

9 Writer

def negate(a)
  [!a, "Not #{a} is #{!a}"]
end

def even(a)
  [a.even?, "Even #{a} is #{a.even?}"]
end

def odd(a)
  r1, log1 = even(20)
  r2, log2 = negate(r1)
  [r2, [log1, log2].join(', ')]
end

odd(5)
def compose_writer(m1, m2)
  lambda do |a|
    r1, log1 = m1.call(a)
    r2, log2 = m2.call(r1)
    [r2, [log1, log2].join(', ')]
  end
end

negate = -> (a) { [!a, "Not #{a} is #{!a}"] }
even = -> (a) { [a.even?, "Even #{a} is #{a.even?}"] }

compose_writer(even, negate).call(20)

10 Considerações

A categoria das Mônadas é apenas uma categoria.

Matemática nos ajuda a ver e definir as coisas enquanto programamos.

11 Recomendações

Author: Gabriela Moreira Mafra

Created: 2021-03-30 Tue 17:44

Validate