* Type No Haskell tambem conseguimos declarar aliases para tipos customizaveis, a vantagem disso sera para nao repetir logicas perante o nosso codigo e tambem deixar ele mais declarativo, dando nomes para declaracoes conseguimos que outras pessoas consigam compreender melhor o nosso codigo. Segue um exemplo de caso nao usarmos: #+begin_src haskell :{ troco :: (Integer, String, Double) -> (Integer, String, Double) -> Double troco (idProd, nome, preco) (idCli, nomeCli, pago) = pago - preco :} #+end_src Dessa forma declarada, conseguimos saber o que cada um significa e a diferenca entre eles, porem para alguem que deseja utilizar essa funcao e querer usar ela, pode ficar um pouco confusa em saber como podera utilizar e que ordem passar os argumentos, ja que nao temos como diferencias o dados passados sem olhar a implementacao #+begin_src haskell prod = (1, "queijo", 100) cli = (1, "Lucas", 200) troco prod cli troco cli prod #+end_src Criando um custom type, podemos criar tipos com aliases que melhor descrevem os argumentos que nossa funcao devera receber e o que cada um significa. Para declararmos um custom type utilizamos a palavra reservada ~type~ do Haskell. Utilizando no exemplo acima a funcao poderia ficar assim: #+begin_src haskell type Produto = (Integer, String, Double) type Cliente = (Integer, String, Double) :{ troco :: Produto -> Cliente -> Double troco (idProd, nome, preco) (idCli, nomeCli, pago) = pago - preco :} #+end_src Desta forma podemos deixar mais claro a intencao da nossa funcao e seus argumentos Porem, ainda assim o usuario ainda pode inverter a ordem dos dados passados, ele pode passar um ~Cliente~ no lugar do ~Produto~ e nossa funcao ira aceitar, porem ira retornar o valor errado. Como apenas criamos um alias para as funcoes que possuem o mesmo tipo, o Haskell nao ira diferenciar os tipos pelos aliases, o que ira ocorrer na hora da compilacao do projeto sera a seguinte etapa: 1. Ira substituir no programa todos os lugares que apontam para os aliases e colocar o tipo 2. Ira realizar a validacao dos tipos para ver se batem Com isso, como nossos tipos sao a mesma tupla, a linguagem nao ira diferenciar e ira aceitar. ** Generic Tambem podemos declarar aliases de forma generica, sem especificar o tipo hardcoded e deixando apenas para quando formos implementar/usar. Para declarar eh algo bem semelhante a declaracao de funcao: #+begin_src haskell type Assoc k v = [(k,v)] :{ find :: Eq k => k -> Assoc k v -> v find k t = head [v | (k', v) <- t, k'==k] :} #+end_src Desta forma criamos o alises ~Assoc~ que recebe dois valores de forma generica, nao precisamos definir o ~k~ ou o ~v~, essa definicao soh sera feita quando os dados que forem passados para a funcao. ** Recursion Nao podemos criar types de forma recursiva, entao a seguinte declaracao de uma arvore nao eh possivel com types: #+begin_src haskell type binaryTree a = (binaryTree a, a, binaryTree a) #+end_src * Custom types Tambem podemos declarar tipos customizaveis #+begin_src haskell data Shape = Circle Float Float Float | Rectangle Float Float Float Float #+end_src #+RESULTS: : Rectangle :: Float -> Float -> Float -> Float -> Shape #+begin_src haskell :{ surface :: Shape -> Float surface (Circle _ _ r) = pi * r ^ 2 surface (Rectangle x1 y1 x2 y2) = (abs $ x2 - x1) * (abs $ y2 - y1) :} surface $ Circle 10 20 10 surface $ Rectangle 0 0 100 100 #+end_src #+RESULTS: : 10000.0 Utilizando o =derinving= keyword podemos fazer com que o Haskell consiga printar os nossos tipos customizaveis junto com seus valores #+begin_src haskell data Shape = Circle Float Float Float | Rectangle Float Float Float Float deriving (Show) (Circle 10 20 5, Rectangle 50 230 60 90) #+end_src #+RESULTS: : Prelude> (Circle 10.0 20.0 5.0,Rectangle 50.0 230.0 60.0 90.0) * Record syntax Record syntax eh utilizado para facilitar na hora de criarmos types mais complexos e com mais dados. Imagine um type do tipo =Person= onde iremos precisar do primeiro nome, ultimo nome, idade, altura, telefone and sorvete favorito. Podemos descrever esse tipo desta forma; #+begin_src haskell data Person = Person String String Int Float String String deriving (Show) let guy = Person "Buddy" "LastName" 43 184.2 "1234-5678" "Chocolate" guy #+end_src #+RESULTS: : Prelude> Prelude> Person "Buddy" "LastName" 43 184.2 "1234-5678" "Chocolate" Mas se usarmos o =record syntax= para declarar a o mesmo tipo, temos uma sintaxe mais amigavel e facil de ler #+begin_src haskell :{ data Person = Person { firstName :: String , lastName :: String , age :: Int , height :: Float , phoneNumber :: String , flavour :: String } deriving (Show) :} let guy = Person { firstName = "Buddy 2", lastName = "LastName", age = 43, height = 184.2, phoneNumber = "1234-5678", flavour = "Chocolate" } guy #+end_src #+RESULTS: : Prelude> Prelude> Person {firstName = "Buddy 2", lastName = "LastName", age = 43, height = 184.2, phoneNumber = "1234-5678", flavour = "Chocolate"} A vantagem tambem aparece na hora de printarmos o tipo, onde tambem podemos ver os campos e seus repectivos valores. ** Estrutura de dados recursiva Vamos criar uma arvore binaria em Haskell :) #+begin_src haskell :{ data Tree a = EmptyTree | Node a (Tree a) (Tree a) deriving (Show, Read, Eq) treeInsert :: (Ord a) => a -> Tree a -> Tree a treeInsert x EmptyTree = Node x EmptyTree EmptyTree treeInsert x (Node a left right) | x == a = Node x left right | x < a = Node a (treeInsert x left) right | x > a = Node a left (treeInsert x right) treeElem :: (Ord a) => a -> Tree a -> Bool treeElem x EmptyTree = False treeElem x (Node a left right) | x == a = True | x < a = treeElem x left | x > a = treeElem x right :} let nums = [8,6,4,1,7,3,5] let numsTree = foldr treeInsert EmptyTree nums treeElem 8 numsTree #+end_src #+RESULTS: : Prelude> Prelude> Prelude> True * Typeclasses A typeclass defines some behavior (like comparing for equality, comparing for ordering, enumeration) and then types that can behave in that way are made instances of that typeclass. The behavior of typeclasses is achieved by defining functions or just type declarations that we then implement. So when we say that a type is an instance of a typeclass, we mean that we can use the functions that the typeclass defines with that type. #+begin_src haskell :{ class Eq a where (==) :: a -> a -> Bool (/=) :: a -> a -> Bool x == y = not (x /= y) x /= y = not (x == y) data TrafficLight = Red | Yellow | Green instance Eq TrafficLight where Red == Red = True Green == Green = True Yellow == Yellow = True _ == _ = False instance Show TrafficLight where show Red = "Red light" show Green = "Green light" show Yellow = "Yellow light" :} Red #+end_src #+RESULTS: : Prelude> Red light ** Yes-no typeclasses Let's create the way that the javascript treats values as truthy and falsy in Haskell, for exemple, the following examples: #+begin_src javascript if (0) console.log("YEAH!") else console.log("NO!") if ("") console.log("YEAH!") else console.log("NO!") if (false) console.log("YEAH!") else console.log("NO!") if ("WHAT") console.log("YEAH!") else console.log("NO!") #+end_src In Haskell, we can only use the value inside a =if= when is of type =Bool=, but now let's implement a function that'll have this behaviour: #+begin_src haskell :{ class YesNo a where yesno :: a -> Bool instance YesNo Int where yesno 0 = False yesno _ = True instance YesNo [a] where yesno [] = False yesno _ = True instance YesNo Bool where yesno = id instance YesNo (Maybe a) where yesno (Just _) = True yesno Nothing = False yesnoIf :: (YesNo y) => y -> a -> a -> a yesnoIf yesnoVal yesResult noResult = if yesno yesnoVal then yesResult else noResult :} yesnoIf (Just 500) "YEAH!" "NO!" #+end_src #+RESULTS: : Prelude> "YEAH!"