Refined.

Contratos en los tipos.

def promedio (nums: List[Int]): Int = nums.reduce(_ + _) / nums.length

Que podría ir mal aquí?

Como podríamos resolverlo?

def promedio (nums: List[Int]): Int = {
  if (nums.isEmpty) 
    throw IllegalArgumentException("La lista no puede ser vacía") 

  nums.reduce(_ + _) / nums.length
}

Y esto:

Se puede mejorar?

def promedio (nums: NonEmptyList[Int]): Int = nums.reduce(_ + _) / nums.length

Que tal esto?

Type Refinement

  • Contratos (Pre,Post Condiciones. Invariantes)
  • Dependent Type System
  • Demostración interactiva de teoremas
  • Análisis Estático del Código

Type Refinement

  • Javascript (Flow de Facebook, refscript, Djs)
  • Scala (Refined)
  • Idris / Coq / AGDA
  • Haskell (Liquid Haskell)

Mainstream!

def definition (word: String, dict: Map[String, String]): String = dict(word)

Otra situación:

Como podríamos resolverlo?

def definition (word: String, dict: Map[String, String]): Option[String] = 
  dict.get(word)

Y esto:

Se puede mejorar?

def definition (word: String)
               (dict: Map[String, String] Refined Contains[word.type])
  : String = dict(word)

Que tal esto:

def at (idx: Int, list: List[String]): String = list(idx)

def fraction (num: Int, div: Int): (Int, Int) = (num, div)

at(9, 1 :: 2 :: 3 :: Nil) // No compila

fraction(1, 0) // No compila

Dos más:

type NonZero = Or[Positive, Negative]

case class Fraction(numerator: Int, denominator: Int Refined NonZero)

Fraction(1, 3)

Fraction(1, 0) //Does not compile

Fraction:

class BetweenAux[N <: Int](start: Witness.Aux[N]) {
  def apply(end: Int Refined Greater[N]): Int Refined Positive = 
    Refined.unsafeApply(end - start.value)
}

def between(n: Int) = new BetweenAux(Witness(n))

between(2)(5)

between(6)(1) //Does not compile

Between:

Flow

Scastie

Typestate analysis

Uniqueness Types / Linear Types

@unique : Variable, parametro o resultado único

@transient : parametro no consumible (prestado)

@peer(x) : Parametro o resultado en la misma región que x

capture(x, y) : Transfiere x a la región de y y retorna un alias de x

swap(x.f, y) : Intercambia dos valores únicos

Rastreo de Recursos

(via plugin de compilador en scala)

val buf: ArrayBuffer[Int] @unique = new ArrayBuffer[Int]
buf += 5
consume buf
buf.remove(0) // No compila
let v = vec![1, 2, 3];

let v2 = v;

println!("v[0] is: {}", v[0]); // No compila

Scala

Rust

class LogList {
  var elem: LogFile = null
  var next: LogList = this
  @transient def add(file: LogFile @peer(this)) =
    if (isEmpty) { elem = file; next = new LogList }
    else next.add(file)
}
val logList: LogList @unique = new LogList
for (test <- tests) {
  val logFile: LogFile @unique = createLogFile(test)
  // realizar computación...
  logList.add(capture(logFile, logList))
}

Transferencias más elaboradas

Máquinas de Estado en los Tipos

import Control.ST

data Access = LoggedOut | LoggedIn
data LoginResult = OK | BadPassword

interface DataStore (m : Type -> Type) where
  data Store : Access -> Type

  connect : ST m Var [add (Store LoggedOut)]
  disconnect : (store : Var) -> ST m () [remove store (Store LoggedOut)]
  
  readSecret : (store : Var) -> ST m String [store ::: Store LoggedIn]
  login : (store : Var) ->
          ST m LoginResult [store ::: Store LoggedOut :->
                             (\res => Store (case res of
                                                  OK => LoggedIn
                                                  BadPassword => LoggedOut))]
  logout : (store : Var) ->
           ST m () [store ::: Store LoggedIn :-> Store LoggedOut]

getData : (ConsoleIO m, DataStore m) => ST m () []
getData = do st <- connect
             OK <- login st
                | BadPassword => do putStrLn "Failure"
                                    disconnect st
             secret <- readSecret st
             putStrLn ("Secret is: " ++ show secret)
             logout st
             disconnect st

-- badGet : DataStore m => ST m () []
-- badGet = do st <- connect
--             secret <- readSecret st
--             disconnect st

DataStore IO where
  Store x = State String -- represents secret data

  connect = do store <- new "Secret Data"
               pure store

  disconnect store = delete store

  readSecret store = readSecret store

  login store = do putStr "Enter password: "
                   p <- getStr
                   if p == "Mornington Crescent"
                      then pure OK
                      else pure BadPassword
  logout store = pure ()

main : IO ()
main = run getData

Referencias:

Rodolfo Hansen @kryptt

Made with Slides.com