scalamx / scala_school

Lessons in the Fundamentals of Scala
http://twitter.github.com/scala_school
4 stars 14 forks source link

Como usuario puedo ver traducida la sección Tipos y Polimorfismo #7

Closed poguez closed 8 years ago

ghost commented 9 years ago

Hola!

poguez commented 9 years ago

Hey, te voy a ayudar con la traducción.

poguez commented 9 years ago

Buenísima @2silval :smile: el jueves nos vemos para que hagas el pull request!

ghost commented 9 years ago

Tipo y polimorfismo básico

Esta lección cubre:

¿Cuáles son los tipos estáticos? ¿Por qué son útiles?

De acuerdo a Pierce: “Es un tipo de sistema con un método sintáctico para comprobar automáticamente la ausencia de ciertos comportamientos erróneos mediante la clasificación de las frases del programa de acuerdo a los tipos de valores que computan"

Los tipos te permiten denotar las funciones de un dominio y codominio. Por ejemplo, de las matemáticas, que estamos acostumbrando a ver:

f: R -> N

Estos nos dice que la función “f” mapa a partir del conjunto de los números reales a los valores del conjunto de los números naturales.

En los abstracto, esto es exactamente lo que los tipos de concreto son. Sistemas de tipo que nos dan algunas formas más poderosas para expresas estos conjuntos.

Dadas estas anotaciones, el compilador no puede estéticamente (En tiempo de compilación) verificar que el programa este correcto. Es decir, el compilador fallará si los valores (en tiempo de ejecución) no cumplen las restricciones impuestas por el programa.

En términos generales, la typechecker sólo puede garantizar que los programas erróneos no sean compilados. No puede garantizar que todos los programas correctos sean compilados.

Con el aumento de las expresividad en los sistemas de tipo, podemos producir código más fiable una que nos permite probar invariantes sobre nuestro programa antes de que incluso corra el programa (Por supuesto, en los bugs de los tipos de modelos). La academia esta empujando los limites de la expresividad. Incluyendo los tipos de valor dependiente!

Toma en cuenta que toda la información de tipo se retira en el tiempo de compilación. Ya que no es necesaria. Eso se le llama eliminación,

Tipos en Scala

El poderoso sistema de Scala permite una expresión rica. Algunos de sus características principales son:

El polimorfismo paramétrico

El polimorfismo se utiliza para escribir código genérico (para valores de diferentes tipos) sin comprometer la riqueza de los tipos estáticos.

Por ejemplo, sin el polimorfismo paramétrico, una estructura de lista de datos genérica sería representada en este aspecto (y de hecho se representa en este aspecto en Java antes de los genéricos):

scala> 2 :: 1 :: "bar" :: "foo" :: Nil
res5: List[Any] = List(2, 1, bar, foo)

Ahora no podemos recuperar cualquier información de los miembros individuales:

scala> res5.head
res6: Any = 2

Y así nuestra aplicación se volvería en una serie de casts (“asinstanceOf[]” y careceríamos de seguridad (ya que estos tipos son todos dinámicos)

El polimorfismo se logra a través de la especificación de las variables de tipo.

scala> def drop1[A](l: List[A]) = l.tail
drop1: [A](l: List[A])List[A]
scala> drop1(List(1,2,3))
res1: List[Int] = List(2, 3)

Scala tiene rank-1 polimorfismo

A grandes rasgos, esto significa que hay algunos conceptos de tipo que te gustaría expresar en Scala que son "demasiado genérico" para el compilador de entender. Supongamos que tenemos una función:

def toList[A](a: A) = List(a)

por lo cual te gustaría usar de manera genérica:

def foo[A, B](f: A => List[A], b: B) = f(b)

Esto no se compila, porque todas las variables de tipo deben ser fijados en el sitio invocación. Incluso si "expusiera" tipo B,

def foo[A](f: A => List[A], i: Int) = f(i)

…obtines un tipo erroneo.

La inferencia de tipos

Una oposición tradicional de tipos estáticos es que tiene tanta sobrecarga sintáctica. Scala alivia esto proporcionando la inferencia de tipos.

El método clásico para la inferencia de tipos de lenguajes de programación funcional es Hindley-Milner, y fue empleado por primera vez en ML.

Funciona un poco diferente, pero es similar en espíritu: inferir limitaciones, y tratar de unificar a un tipo.Mientras que en OCaml, puede:

El sistema de inferencia de tipos de Scala En Scala, por ejemplo, no se puede hacer lo siguiente:

scala> { x => x }
:7: error: missing parameter type
       { x => x }

Mientras que en OCaml, puede:

# fun x -> x;;
- : 'a -> 'a = 

En scala todo tipo de inferencia es local. Scala considera una expresión a la vez. Por ejemplo:

scala> def id[T](x: T) = x
id: [T](x: T)T
scala> val x = id(322)
x: Int = 322
scala> val x = id("hey")
x: java.lang.String = hey
scala> val x = id(Array(1,2,3,4))
x: Array[Int] = Array(1, 2, 3, 4)

Tipos ahora se conservan, El compilador de Scala infiere el parámetro de tipo para nosotros. Ten en cuenta también cómo no teníamos que especificar el tipo de retorno de forma explícita.

Variación

El sistema de Scala tiene que tomar en cuenta de las jerarquías de clase junto con el polimorfismo. Jerarquías de clases permiten la expresión de las relaciones de subtipos. Una pregunta fundamenta es, ¿si el Container[T´] es considerado una subclase del Container[T]? Las anotaciones de Variaciones te permiten expresar la siguiente relación entre las clases de jerarquía y los tipos de polimorficos.

Significado Notación Scala
covariante C[T’] es una sub - clase de C[T] [+T]
contravariante C[T] es una sub - clase de C[T’] [-T]
invariante C[T] y C[T’] no están relacionados [T]

La relación subtipo realmente significa: para un determinado tipo T, si T 'es un subtipo, puede ser sustituido?

scala> class Covariant[+A]
defined class Covariant
scala> val cv: Covariant[AnyRef] = new Covariant[String]
cv: Covariant[AnyRef] = Covariant@4035acf6
scala> val cv: Covariant[String] = new Covariant[AnyRef]
:6: error: type mismatch;
 found   : Covariant[AnyRef]
 required: Covariant[String]
       val cv: Covariant[String] = new Covariant[AnyRef]
                                   ^
scala> class Contravariant[-A]
defined class Contravariant
scala> val cv: Contravariant[String] = new Contravariant[AnyRef]
cv: Contravariant[AnyRef] = Contravariant@49fa7ba
scala> val fail: Contravariant[AnyRef] = new Contravariant[String]
:6: error: type mismatch;
 found   : Contravariant[String]
 required: Contravariant[AnyRef]
       val fail: Contravariant[AnyRef] = new Contravariant[String]
                                     ^

Contravarianza parece extraño. Cuando se utiliza? Algo sorprendente!

trait Function1 [-T1, +R] extends AnyRef

Si piensas en esto desde el punto de vista de la sustitución, tiene mucho sentido. Primero vamos a definir una jerarquía de clases simple:

scala> class Animal { val sound = "rustle" }
defined class Animal
scala> class Bird extends Animal { override val sound = "call" }
defined class Bird
scala> class Chicken extends Bird { override val sound = "cluck" }
defined class Chicken

Supongamos que necesitas una función que tome un parámetro de Bird:

scala> val getTweet: (Bird => String) = // TODO

La biblioteca animal estándar tiene una función que hace lo que quiere, pero se necesita un parámetro Animal. En la mayoría de las situaciones, si usted dice que "necesito un , tengo una subclase de ", estás bien. Pero los parámetros de la función son contravariante. Si necesita una función que toma un Bird y tiene una función que toma un Chicken, esa función se fijara en un Duck.. Pero una función que toma un Animal está bien:

scala> val getTweet: (Bird => String) = ((a: Animal) => a.sound )
getTweet: Bird => String = 

Tipo de valor de retorno de una función es covariante. Si tienes una función que regresa a un Bird pero tienes una función que regresa a Chicken,, eso esta bien.

scala> val hatch: (() => Bird) = (() => new Chicken )
hatch: () => Bird = 

Límites

Scala permite restringir las variables polimórficas utilizando límites. Estos límites expresan las relaciones de subtipos.

scala> def cacophony[T](things: Seq[T]) = things map (_.sound)
:7: error: value sound is not a member of type parameter T
       def cacophony[T](things: Seq[T]) = things map (_.sound)
                                                        ^
Límites de tipo más bajas también son compatibles; son práctico con la contravarianza y covarianza inteligente. List[+T] es covariante; una lista de las Aves es una lista de los Animales. List  define una operación ::(elem T) que refleja una nueva List  con elem  fijados. La List nueva tiene los mismos tipos que la original. 
scala> def biophony[T <: Animal](things: Seq[T]) = things map (_.sound)
biophony: [T <: Animal](things: Seq[T])Seq[java.lang.String]
scala> biophony(Seq(new Chicken, new Bird))
res5: Seq[java.lang.String] = List(cluck, call)

Lista tambien define ::[B >: T](x: B) lo cual refleja a List[B].. Observa que B >: T.. Ese tipo especifico B es una clase superior de T. Que nos permite ejecutar la accione correcta cuando buscamos a Animal en la List[Bird]:

scala> new Animal :: flock
res59: List[Animal] = List(Animal@11f8d3a8, Bird@7e1ec70e, Bird@169ea8d2)

Observa que el tipo de retorna es List[Animal].

Cuantificación

A veces no le importa ser capaz de nombrar una variable de tipo, por ejemplo:

scala> def count[A](l: List[A]) = l.size
count: [A](List[A])Int

En su lugar se puede usar “wildcards":

scala> def count(l: List[_]) = l.size
count: (List[_])Int

Este es un hack para:

scala> def count(l: List[T forSome { type T }]) = l.size
count: (List[T forSome { type T }])Int

Tenga en cuenta que la cuantificación puede ser complicado:

scala> def drop1(l: List[_]) = l.tail
drop1: (List[_])List[Any]

De repente perdimos información de tipo! Para ver lo que está pasando, puedes revertir la sintaxis pesada:

scala> def drop1(l: List[T forSome { type T }]) = l.tail
drop1: (List[T forSome { type T }])List[T forSome { type T }]

No podemos decir nada acerca de T porque el tipo no lo permite.

También se puede aplicar límites a la variables de tipo “wildcard":

scala> def hashcodes(l: Seq[_ <: AnyRef]) = l map (_.hashCode)
hashcodes: (Seq[_ <: AnyRef])Seq[Int]
scala> hashcodes(Seq(1,2,3))
:7: error: type mismatch;
 found   : Int(1)
 required: AnyRef
Note: primitive types are not implicitly converted to AnyRef.
You can safely force boxing by casting x.asInstanceOf[AnyRef].
       hashcodes(Seq(1,2,3))
                     ^
scala> hashcodes(Seq("one", "two", "three"))
res1: Seq[Int] = List(110182, 115276, 110339486)

Recurso adicional: Existential types in Scala by D. R. MacIver