Blogg
Här finns tekniska artiklar, presentationer och nyheter om arkitektur och systemutveckling. Håll dig uppdaterad, följ oss på LinkedIn
Här finns tekniska artiklar, presentationer och nyheter om arkitektur och systemutveckling. Håll dig uppdaterad, följ oss på LinkedIn
Efter två intensiva men mycket intressanta dagar på Scala eXchange i London har vi fått se två mycket bra sammanfattningar av hela konferensen i och med Andreas Mossljungs och Pär Wenåkers bloggposter. Jag tänkte därför istället skriva om ett ämne som på ett eller annat sätt återkom i många av presentationerna - mönster för funktionell programmering.
Dessa är motsvarigheten till objektorienteringens mönster och kallas för Type Classes. Många av dessa mönster grundar sig i matematikens kategoriteori och används bl.a. för att uppnå kompositionalitet. I denna blogpost kommer jag lämna de matematiska teorierna därhän och istället på ett lite mer pragmatiskt sätt försöka förklara hur de används. Jag kommer att utgå från den kanske mest grundläggande av mönstren, Monoid.
Vill man läsa om fler mönster och gå lite mer på djupet jag rekommendera Daniela Sfregolas presentation samt boken “Scala with cats”. Är man intresserad av matematiken bakom kan jag varmt rekommendera Bartosz Milewskis mycket pedagogiska keynote i ämnet.
Monoid är en typklass som beskriver ett mönster för hur man kan kombinera två objekt av samma typ och det kan finnas många olika implementationer, t.ex. för Int, String och List. En monoid har några dessutom viktiga egenskaper som är viktiga att förstå:
Den första brukar namnges combine och brukar beskriva hur man kombinerar två objekt av samma typ och kan se ut så här:
def combine[A](x: A, y: A): A
Den andra egenskapen är ett identitetselement och fungerar på så sätt att om man kombinerar ett objekt med sin så kallade identitet så returneras alltid ursprungsobjektet. Den brukar beskrivas:
def identity[A]: A
En monoid är också alltid associativ, dvs. om man kombinerar flera element så spelar det inte någon roll i vilken ordning man utför operationerna (detta skiljer sig från om något är kommutativt, då inte heller ordningen på operanderna spelar någon roll) t.ex:
(2 + 3) + 4 == 9 == 2 + (3 + 4)
(2 * 3) * 4 == 24 == 2 * (3 * 4)
("A" + "B") + "C" == "ABC" == "A" + ("B" + "C") //Ej kommutativ
Sammanfattar man dessa egenskaper i en trait så skulle den kunna se så här:
trait Monoid[A] {
def combine(x: A, y: A): A
def identity: A
}
Nedan följer några exempel för hur några implementationer av en sådan typklass skulle kunna se ut:
Kombinera: 1 + 2 == 3
Identitet: 0
class IntAddition extends Monoid[Int] {
def combine(x: Int, y: Int): Int = x + y
def identity: Int = 0
}
Kombinera: 2 * 3 == 6
Identitet: 1
class IntMultiplication extends Monoid[Int] {
def combine(x: Int, y: Int): Int = x * y
def identity: Int = 1
}
Kombinera: "Hello" + " World" == "Hello World"
Identitet: ""
class StringConcat extends Monoid[String] {
def combine(x: String, y: String): String = x + y
def identity: String = ""
}
På detta sätt kan man alltså abstrahera över några mycket grundläggande egenskaper som vissa typer har. Detta kan man sedan utnyttja för att komponera mer komplexa funktioner på ett generiskt sätt. T.ex. kan man tänka sig en funktion som slår ihop en lista med objekt som vi kan döpa till addAll:
/**
Definition av en funktion som adderar/slår ihop en lista av objekt som har monoid-egenskaper.
*/
def addAll[A](l: List[A], m: Monoid[A]):A = l.foldRight(m.identity)(m.combine)
val answer1 = addAll(List(1, 2, 3, 4, 5), new IntAddition())
// answer1: Int = 15
val answer2 = addAll(List(1, 2, 3, 4, 5), new IntMultiplication())
// answer2: Int = 120
val answer3 = addAll(List("A", "B", "C", "D"), new StringConcat())
// answer3: String = "ABCD"
Ett par andra viktiga typklasser som man ofta stöter på är Functor och Monad men dessa får jag inte plats med i denna post. Det finns dock mycket bra förklaringar på dessa samt ytterligare någon typklass i tidigare nämna referenser. Jag kan varm rekommendera att lägga lite tid på att titta på dessa presentationer om man vill fördjupa sig lite. Jag hoppas dock att jag lyckats kasta lite ljus över Monoiden och att det blivit lite mer begripligt varför typklasser kan vara nyttiga.