Skip to content

Instantly share code, notes, and snippets.

@chemacortes
Created April 8, 2025 23:56
Show Gist options
  • Save chemacortes/8bf315a9d7230a1464891730d84ddc72 to your computer and use it in GitHub Desktop.
Save chemacortes/8bf315a9d7230a1464891730d84ddc72 to your computer and use it in GitHub Desktop.
Deseo usar scala3 como lenguaje formal para describir fundamentos de álgebra computacional y teoría de números. Una buena introducción puede ser el repositorio https://github.com/zplot/medium/tree/master/src/main/scala/com/algebra . ¿Cómo debería continuar para completar el resto de definiciones? Un ejemplo concreto: la definición de anillo conm…

¡Excelente iniciativa! Usar Scala 3, con su potente sistema de tipos (incluyendo traits, intersection types, extension methods, context functions y given/using), es una forma muy elegante y rigurosa de formalizar conceptos algebraicos. El enfoque de "type classes" encaja perfectamente aquí.

El repositorio que mencionas parece un buen punto de partida, probablemente definiendo estructuras básicas como Semigrupos, Monoides y Grupos. Para continuar y definir estructuras más complejas como los anillos, seguirías construyendo sobre esas bases.

Cómo Continuar - Pasos Generales:

  1. Jerarquía de Traits: Define cada estructura algebraica como un trait. Las estructuras más complejas heredarán (extends) de las más simples. Por ejemplo, un Ring extenderá (de alguna forma) las propiedades de un AbelianGroup (para la suma) y un Monoid (para la multiplicación).
  2. Métodos y Operadores: Usa extension methods dentro de los traits para definir las operaciones binarias (+, *) y unarias (-) de forma natural e infija.
  3. Identidades: Define métodos (zero, one) dentro de los traits apropiados (Monoid, AdditiveMonoid, MultiplicativeMonoid) para los elementos neutros.
  4. Composición: Combina los traits necesarios para definir estructuras compuestas. Un anillo necesita una estructura aditiva y una multiplicativa, conectadas por la distributividad.
  5. Context Parameters (using): Para escribir funciones genéricas que operen sobre cualquier tipo T que tenga una estructura algebraica específica (p.ej., un Ring), usarás parámetros de contexto: def myFunction[T](x: T, y: T)(using ring: Ring[T]): T = ....
  6. Instancias (given): Para tipos concretos (como Int, BigInt, polinomios, etc.), proporcionarás instancias given que implementen los traits correspondientes. given IntRing: Ring[Int] with { ... }.
  7. Leyes Algebraicas: ¡Muy importante! Los traits definen la firma de las operaciones, pero no garantizan que las leyes (asociatividad, conmutatividad, distributividad, etc.) se cumplan. Deberías usar una biblioteca de testing basada en propiedades como ScalaCheck para escribir tests que verifiquen que tus instancias given realmente satisfacen las leyes requeridas para cada estructura.

Ejemplo Concreto: Definición de Anillo Conmutativo en Scala 3

Siguiendo la filosofía de construir sobre estructuras existentes, podríamos tener algo así:

// Asumimos que ya existen definiciones para:
// trait AdditiveSemigroup[A]: extension (a: A) def +(b: A): A
// trait AdditiveMonoid[A] extends AdditiveSemigroup[A]: def zero: A
// trait AdditiveGroup[A] extends AdditiveMonoid[A]: extension (a: A) def unary_- : A
// trait AdditiveAbelianGroup[A] extends AdditiveGroup[A] // Solo añade la ley conmutativa

// trait MultiplicativeSemigroup[A]: extension (a: A) def *(b: A): A
// trait MultiplicativeMonoid[A] extends MultiplicativeSemigroup[A]: def one: A
// trait MultiplicativeCommutativeMonoid[A] extends MultiplicativeMonoid[A] // Solo añade la ley conmutativa

// ---------------------------------------------------
// Definición del Anillo (Ring)
// ---------------------------------------------------
/**
 * Representa la estructura de Anillo.
 * Un Anillo (R, +, *) requiere:
 * 1. (R, +) sea un Grupo Abeliano (asociatividad, identidad 'zero', inverso 'unary_-', conmutatividad).
 * 2. (R, *) sea un Monoide (asociatividad, identidad 'one').
 * 3. La multiplicación distribuye sobre la suma. a*(b+c) == a*b + a*c y (a+b)*c == a*c + b*c
 */
trait Ring[A] extends AdditiveAbelianGroup[A], MultiplicativeMonoid[A]:
  // Hereda:
  // De AdditiveAbelianGroup: extension (+), def zero, extension (unary_-)
  // De MultiplicativeMonoid: extension (*), def one

  // Las leyes (distributividad, etc.) no se pueden forzar en el tipo,
  // deben verificarse (p. ej., con tests de propiedades).
end Ring

// ---------------------------------------------------
// Definición del Anillo Conmutativo (Commutative Ring)
// ---------------------------------------------------
/**
 * Representa la estructura de Anillo Conmutativo.
 * Un Anillo Conmutativo (R, +, *) es un Anillo donde la multiplicación (*)
 * también es conmutativa.
 * Es decir, extiende Ring y requiere que (R, *) sea un Monoide Conmutativo.
 */
trait CommutativeRing[A] extends Ring[A], MultiplicativeCommutativeMonoid[A]
  // Hereda todos los métodos y propiedades de Ring.
  // La herencia de MultiplicativeCommutativeMonoid añade el requisito
  // de la ley de conmutatividad para la multiplicación.
end CommutativeRing

// ---------------------------------------------------
// Ejemplo de Instancia para Int
// ---------------------------------------------------
given IntCommutativeRing: CommutativeRing[Int] with
  // Implementación de AdditiveAbelianGroup para Int
  extension (a: Int) def +(b: Int): Int = a + b
  def zero: Int = 0
  extension (a: Int) def unary_- : Int = -a
  // La suma de Int es asociativa, tiene neutro 0, inverso -, y es conmutativa.

  // Implementación de MultiplicativeCommutativeMonoid para Int
  extension (a: Int) def *(b: Int): Int = a * b
  def one: Int = 1
  // La multiplicación de Int es asociativa, tiene neutro 1, y es conmutativa.

  // La distributividad se cumple para Int: a*(b+c) == a*b + a*c
end IntCommutativeRing

// ---------------------------------------------------
// Ejemplo de Uso
// ---------------------------------------------------
// Una función genérica que opera sobre cualquier Anillo Conmutativo
def polynomialExpansion[T](a: T, b: T)(using ring: CommutativeRing[T]): T = {
  import ring.* // Importa los extension methods (+, *, unary_-) y miembros (zero, one)

  // Podemos usar la notación infija gracias a los extension methods
  // Calcula (a + b)^2 = a*a + 2*a*b + b*b
  // Nota: '2' debe definirse en el contexto del anillo. Asumimos una función fromInt.
  // Para simplificar aquí, haremos a*a + a*b + b*a + b*b
  // que por conmutatividad es a*a + a*b + a*b + b*b
  val a_sq = a * a
  val b_sq = b * b
  val ab = a * b
  //val two = ring.one + ring.one // Forma de obtener '2' en el anillo
  // a_sq + (two * ab) + b_sq // (a+b)^2
  a_sq + ab + ab + b_sq // Usando a*b + b*a y la conmutatividad ya implícita
}

@main def main(): Unit = {
  val x = 3
  val y = 5
  // El compilador encuentra 'IntCommutativeRing' automáticamente
  val result = polynomialExpansion(x, y) 
  println(s"($x + $y)^2 = $result") // (3+5)^2 = 8^2 = 64. Calculation: 3*3 + 3*5 + 3*5 + 5*5 = 9 + 15 + 15 + 25 = 64

  // Ejemplo con BigInt (necesitaríamos un 'given' para BigInt)
  /*
  given BigIntCommutativeRing: CommutativeRing[BigInt] with { ... }
  val bigA = BigInt(100)
  val bigB = BigInt(200)
  val bigResult = polynomialExpansion(bigA, bigB)
  println(s"($bigA + $bigB)^2 = $bigResult")
  */
}

Próximos Pasos Específicos:

  1. Completar Jerarquía: Define sistemáticamente Semigroup, Monoid, Group, AbelianGroup (tanto aditivo como multiplicativo si prefieres separarlos inicialmente, aunque combinarlos como en el ejemplo suele ser práctico para Ring). Define Semiring como paso intermedio si lo ves útil (Monoide aditivo, Monoide multiplicativo, distributividad).
  2. Definir Field (Cuerpo): Un cuerpo es un Anillo Conmutativo donde todo elemento no nulo tiene inverso multiplicativo. Necesitarás añadir inverse o reciprocal para la multiplicación.
    trait Field[A] extends CommutativeRing[A]:
      extension (a: A) def inverse: A // O def reciprocal(a: A): A
      // Ley: Si a != zero, entonces a * inverse(a) == one
      // Requiere una forma de comparar con 'zero'.
  3. Implementar given para Tipos Clave:
    • Int, Long, Double (Cuidado: Double no forma un anillo/cuerpo perfecto por problemas de precisión y IEEE 754, pero se puede definir una instancia "aproximada").
    • BigInt (Anillo Conmutativo).
    • Rational (Necesitarás crear una clase Rational y definir su Field).
    • Polinomios sobre un Anillo/Cuerpo (requiere una clase Polynomial[T] y un given que dependa del given para T).
    • Matrices sobre un Anillo/Cuerpo (requiere clase Matrix[T], Ring para matrices cuadradas, no conmutativo en general).
    • Enteros módulo n (ZmodN) (Anillo Conmutativo, Cuerpo si n es primo).
  4. Verificar Leyes: Escribe tests con ScalaCheck para cada given que implementes, asegurándote de que las propiedades algebraicas se cumplen.
  5. Explorar Bibliotecas Existentes: Echa un vistazo a bibliotecas como Spire o Cats-Kernel. No para usarlas directamente si tu objetivo es aprender y formalizar desde cero, sino para ver cómo han estructurado ellos las jerarquías y resuelto ciertos problemas. Te darán muchas ideas.

Usar Scala 3 para esto es un proyecto ambicioso y muy gratificante. ¡Adelante!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment