Well, I attempted this in Haskell. I took the liberty of making each function return a Maybe value, being Nothing when the team isn't in the table instead of defaulting to 0. It uses a state monad to make storing and "mutating" data feasible. I decided to implement my own state monad here, but there are (better) implementations on stackage.
moduleSoccerLeague(Game,Name,Team,League,empty,push,points,goalsFor,goalsAgainst,goalDifference,wins,draws,losses,State,runState)whereimportData.Map.Strict(Map)importqualifiedData.Map.StrictasMimportData.Bifunctor(bimap)importData.Tuple(swap)importData.Maybe(fromMaybe)importData.Bool(bool)importText.ParserCombinators.Parsec(GenParser,ParseError,parse,many1,digit,letter,string,space,(<|>))typeName=StringdataTeam=Team{t_name::Name,t_goalsFor::Int,t_goalsAgainst::Int,t_wins::Int,t_draws::Int,t_losses::Int}derivingShowtypeLeague=MapNameTeamtypeGame=((Name,Int),(Name,Int))updateTeam::Name->(Int,Int)->StateLeague()updateTeamname(for,against)=domap<-getlett=fromMaybe(Teamname00000)$mapM.!?namelett'=Team{t_name=name,t_goalsFor=t_goalsFort+for,t_goalsAgainst=t_goalsAgainstt+against,t_wins=t_winst+bool01(for>against),t_draws=t_drawst+bool01(for==against),t_losses=t_lossest+bool01(for<against)}put$M.insertnamet'mapempty::Leagueempty=M.emptypush::String->StateLeague()pushstr=caseparseGamestrofLefte->error$"bad push input: "++strRight((n1,s1),(n2,s2))->doupdateTeamn1(s1,s2)updateTeamn2(s2,s1)-- Lifts a simple function on a team in the league to work on the stateliftToState::(Team->a)->String->StateLeague(Maybea)liftToStatefname=get>>=return.fmapf.(flip(M.!?)name)points::String->StateLeague(MaybeInt)points=liftToState$sum.flipfmap[(*3).t_wins,t_draws].flip($)goalsFor::String->StateLeague(MaybeInt)goalsFor=liftToStatet_goalsForgoalsAgainst::String->StateLeague(MaybeInt)goalsAgainst=liftToStatet_goalsAgainstgoalDifference::String->StateLeague(MaybeInt)goalDifference=liftToState(\team->t_goalsForteam-t_goalsAgainstteam)wins::String->StateLeague(MaybeInt)wins=liftToStatet_winsdraws::String->StateLeague(MaybeInt)draws=liftToStatet_drawslosses::String->StateLeague(MaybeInt)losses=liftToStatet_losses-- GAME STRING PARSERtrimSpaces::String->StringtrimSpaces=f.fwheref=dropWhile(==' ').reversenumber::GenParserCharstIntnumber=many1digit>>=return.readname::GenParserCharstNamename=many1(letter<|>space)>>=return.trimSpacesgame::GenParserCharstGamegame=dofstName<-namefstScore<-numberstring" - "sndScore<-numbersndName<-namereturn((fstName,fstScore),(sndName,sndScore))parseGame::String->EitherParseErrorGameparseGame=parsegame"(unknown)"-- STATE MONAD IMPLEMENTATIONnewtypeStatesa=State{runState::s->(s,a)}instanceFunctor(States)wherefmapf(Statex)=State$bimapidf.xinstanceApplicative(States)wherepurex=State$\s->(s,x)(Statesf)<*>(Statesx)=State$\s->let(s',f)=sfs(s'',x)=sxsin(s'',fx)instanceMonad(States)wherereturn=puresx>>=f=State$\s->let(s',x)=runStatesxsinrunState(fx)s'get::Statessget=State$\x->(x,x)put::s->States()puts=State$const(s,())
Additionally, here's the example given in the post translated to my Haskell implementation:
Well, I attempted this in Haskell. I took the liberty of making each function return a
Maybe
value, beingNothing
when the team isn't in the table instead of defaulting to 0. It uses a state monad to make storing and "mutating" data feasible. I decided to implement my own state monad here, but there are (better) implementations on stackage.Additionally, here's the example given in the post translated to my Haskell implementation: