DRY::Monads

Abstraktion the “DRY” way.

Fragt man Bard oder ChatGPT nach der Bedeutung von Monad, wird auf Gottfried Wilhelm Leibniz verwiesen.

  • Monaden werden als unteilbare, immaterielle und metaphysische Substanzen beschrieben, die die fundamentalen Bausteine der Realität darstellen.
  • Jede Monoide hat eine einzigartige Wahrnehmung und spiegelt das gesamte Universum aus ihrer eigenen Perspektive wider.

Monoide werden in der Gruppentheorie verwendet. Ihre zentrale Eigenschaft: Sie geben Ergebnisse zurück, die der gleichen Menge angehören. Sie sind algebraische Strukturen, die wesentliche Eigenschaften von Funktionen kapseln. Konkret werden
if - then - else -end
Strukturen abstrahiert. Damit wird der Programmcode im Hauptprogramm übersichtlicher.

Ein Minimalbeipiel:

class Auth
  extend Dry::Monads[:result]
  def self.authenticate name
      name == 'correct' ?  Success( name: name ) : Failure( error: "No such User" ) 
  end
end

Die Methode Auth.authenticate hat als Input-Größe einen String. Sie gibt entweder ein DRY::Monads::Result::Success oder ein DRY::Monads::Result::Failure-Objekt zurück.

Ruft man die Monad-Klassenmethode auf, dann erhält man das evaluierte Objekt. Dies kann überall dechiffriert werden. Es besteht eine grundsätzliche Verwandschaft mit Schrödingers-Katze.

  a = Auth('Helga')

  a.value! unless a.failure?
  # Ausgabe eines Default-Wertes für failure
  a.value_or "not authenticated" 

  ==> { name: 'Helga' }

Einen Schritt weiter geht die do notaton.

Class Runner
  include Dry::Monads::Do.for(:call)
  def call
      value = A.authenticate( 'correct')
      yield value
  end
end

Ein Aufruf von Runner.new.call gibt entweder die dechiffrierte Success-Antwort zurück oder das DryMonadResult::Failure-Objekt.

Zur Weiterverarbeitung der dechiffrierten Messages bietet das Gem die Methoden bind und fmap. Erstere übergibt die Message an einen Block, letztere stellt einen Block zur Modifizierung bereit.

Class Runner
  include Dry::Monads::Do.for(:call)

  def call
      A.authenticate( 'correct').bind do |value|
        # Anweisungen, die ein Success voraussetzen
      end
  end
end

(to be continued)