moduleSolutions.DataTypes-- If all or almost all functions in a module are provably-- total, it is convenient to add the following pragma-- at the top of the module. It is then no longer necessary-- to annotate each function with the `total` keyword.%defaulttotal---------------------------------------------------------------------------------- Enumerations---------------------------------------------------------------------------------- 1and:Bool->Bool->BoolandTrueb=bandFalse_=Falseor:Bool->Bool->BoolorTrue_=TrueorFalseb=b--2dataUnitOfTime=Second|Minute|Hour|Day|WeektoSeconds:UnitOfTime->Integer->IntegertoSecondsSecondy=ytoSecondsMinutey=60*ytoSecondsHoury=60*60*ytoSecondsDayy=24*60*60*ytoSecondsWeeky=7*24*60*60*yfromSeconds:UnitOfTime->Integer->IntegerfromSecondsus=s`div`toSecondsu1convert:UnitOfTime->Integer->UnitOfTime->Integerconvertu1nu2=fromSecondsu2(toSecondsu1n)--3dataElement=H|C|N|O|FatomicMass:Element->DoubleatomicMassH=1.008atomicMassC=12.011atomicMassN=14.007atomicMassO=15.999atomicMassF=18.9984---------------------------------------------------------------------------------- Sum Types--------------------------------------------------------------------------------dataTitle=Mr|Mrs|OtherStringeqTitle:Title->Title->BooleqTitleMrMr=TrueeqTitleMrsMrs=TrueeqTitle(Otherx)(Othery)=x==yeqTitle__=FalseisOther:Title->BoolisOther(Other_)=TrueisOther_=FalsedataLoginError=UnknownUserString|InvalidPassword|InvalidKeyshowError:LoginError->StringshowError(UnknownUserx)="Unknown user: "++xshowErrorInvalidPassword="Invalid password"showErrorInvalidKey="Invalid key"---------------------------------------------------------------------------------- Records---------------------------------------------------------------------------------- 1recordTimeSpanwhereconstructorMkTimeSpanunit:UnitOfTimevalue:IntegertimeSpanToSeconds:TimeSpan->IntegertimeSpanToSeconds(MkTimeSpanunitvalue)=toSecondsunitvalue-- 2eqTimeSpan:TimeSpan->TimeSpan->BooleqTimeSpanxy=timeSpanToSecondsx==timeSpanToSecondsy-- alternative equality check using `on` from the Idris PreludeeqTimeSpan':TimeSpan->TimeSpan->BooleqTimeSpan'=(==)`on`timeSpanToSeconds-- 3showUnit:UnitOfTime->StringshowUnitSecond="s"showUnitMinute="min"showUnitHour="h"showUnitDay="d"showUnitWeek="w"prettyTimeSpan:TimeSpan->StringprettyTimeSpan(MkTimeSpanSecondv)=showv++" s"prettyTimeSpan(MkTimeSpanuv)=showv++" "++showUnitu++"("++show(toSecondsuv)++" s)"-- 4compareUnit:UnitOfTime->UnitOfTime->OrderingcompareUnit=compare`on`(\x=>toSecondsx1)minUnit:UnitOfTime->UnitOfTime->UnitOfTimeminUnitxy=casecompareUnitxyofLT=>x_=>yaddTimeSpan:TimeSpan->TimeSpan->TimeSpanaddTimeSpan(MkTimeSpanu1v1)(MkTimeSpanu2v2)=caseminUnitu1u2ofu=>MkTimeSpanu(convertu1v1u+convertu2v2u)---------------------------------------------------------------------------------- Generic Data Types---------------------------------------------------------------------------------- 1mapMaybe:(a->b)->Maybea->MaybebmapMaybe_Nothing=NothingmapMaybef(Justx)=Just(fx)appMaybe:Maybe(a->b)->Maybea->MaybebappMaybe(Justf)(Justv)=Just(fv)appMaybe__=NothingbindMaybe:Maybea->(a->Maybeb)->MaybebbindMaybeNothing_=NothingbindMaybe(Justx)f=fxfilterMaybe:(a->Bool)->Maybea->MaybeafilterMaybefNothing=NothingfilterMaybef(Justx)=if(fx)thenJustxelseNothingfirst:Maybea->Maybea->MaybeafirstNothingy=yfirst(Justx)_=Justxlast:Maybea->Maybea->Maybealastxy=firstyxfoldMaybe:(acc->el->acc)->acc->Maybeel->accfoldMaybefx=maybex(fx)-- 2mapEither:(a->b)->Eitherea->EitherebmapEither_(Leftx)=LeftxmapEitherf(Rightx)=Right(fx)appEither:Eithere(a->b)->Eitherea->EitherebappEither(Leftx)_=LeftxappEither(Right_)(Leftx)=LeftxappEither(Rightf)(Rightv)=Right(fv)bindEither:Eitherea->(a->Eithereb)->EitherebbindEither(Leftx)_=LeftxbindEither(Rightx)f=fxfirstEither:(e->e->e)->Eitherea->Eitherea->EithereafirstEitherfun(Lefte1)(Lefte2)=Left(fune1e2)firstEither_(Lefte1)y=yfirstEither_(Rightx)_=Rightx-- instead of implementing this via pattern matching, we use-- firstEither and swap the arguments. Since this would mean that-- in the case of two `Left`s the errors would be in the wrong-- order, we have to swap the arguments of `fun` as well.-- Function `flip` from the prelude does this for us.lastEither:(e->e->e)->Eitherea->Eitherea->EitherealastEitherfunxy=firstEither(flipfun)yxfromEither:(e->c)->(a->c)->Eitherea->cfromEitherf_(Leftx)=fxfromEither_g(Rightx)=gx-- 3mapList:(a->b)->Lista->ListbmapListfNil=NilmapListf(x::xs)=fx::mapListfxsfilterList:(a->Bool)->Lista->ListafilterListfNil=NilfilterListf(x::xs)=iffxthenx::filterListfxselsefilterListfxs(++):Lista->Lista->Lista(++)Nilys=ys(++)(x::xs)ys=x::(Solutions.DataTypes.(++)xsys)headMaybe:Lista->MaybeaheadMaybeNil=NothingheadMaybe(x::_)=JustxtailMaybe:Lista->Maybe(Lista)tailMaybeNil=NothingtailMaybe(x::xs)=JustxslastMaybe:Lista->MaybealastMaybeNil=NothinglastMaybe(x::Nil)=JustxlastMaybe(_::xs)=lastMaybexsinitMaybe:Lista->Maybe(Lista)initMaybel=caselofNil=>Nothingx::xs=>caseinitMaybexsofNothing=>JustNilJustys=>Just(x::ys)foldList:(acc->el->acc)->acc->Listel->accfoldListfunvaccNil=vaccfoldListfunvacc(x::xs)=foldListfun(funvaccx)xs-- 4recordClientwhereconstructorMkClientname:Stringtitle:Titleage:Bits8passwordOrKey:EitherBits64StringdataCredentials=PasswordStringBits64|KeyStringStringlogin1:Client->Credentials->EitherLoginErrorClientlogin1c(Passworduy)=ifc.name==uthenifc.passwordOrKey==LeftythenRightcelseLeftInvalidPasswordelseLeft(UnknownUseru)login1c(Keyux)=ifc.name==uthenifc.passwordOrKey==RightxthenRightcelseLeftInvalidKeyelseLeft(UnknownUseru)login:ListClient->Credentials->EitherLoginErrorClientloginNil(Passwordu_)=Left(UnknownUseru)loginNil(Keyu_)=Left(UnknownUseru)login(x::xs)cs=caselogin1xcsofRightc=>RightcLeftInvalidPassword=>LeftInvalidPasswordLeftInvalidKey=>LeftInvalidKeyLeft_=>loginxscs--5formulaMass:List(Element,Nat)->DoubleformulaMass[]=0formulaMass((e,n)::xs)=atomicMasse*castn+formulaMassxs