Package: hymn
IntroductionHymn is a monad library for Hy/Python, with do notation for monad
comprehension.Code are better than words.The continuation monad=>(importhymn.dsl[cont-mcall/cc])=>;; computations in continuation passing style=>(defndouble[x](cont-m.unit(*x2)))=>(setvlength(cont-m.monadiclen))=>;; chain with bind=>(.run(>>(cont-m.unit[123])lengthdouble))6=>(defnsquare[n](call/cc(fn[k](k(**n2)))))=>(.run(square12))144=>(.run(square12)(fn[x](+x1)))145=>(.run(square12)str)"144"=>(requirehymn.dsl[do-monad-return])=>(.run(do-monad-return[sqr(square42)](.format"answer^2 = {}"sqr)))"answer^2 = 1764"The either monad=>(importhymn.dsl[LeftRighteitherfailsafe])=>(requirehymn.dsl[do-monad-return])=>;; do notation with either monad=>(do-monad-return[a(Right1)b(Right2)](/ab))Right(0.5)=>(do-monad-return[a(Right1)b(LeftNaN)](/ab))Left(nan)=>;; failsafe is a function decorator that wraps return value into either=>(importhy.pyops[/])=>(setvsafe-div(failsafe/))=>;; returns Right if nothing wrong=>(safe-div42)Right(2.0)=>;; returns Left when bad thing happened, like exception being thrown=>(safe-div10)Left(ZeroDivisionError('divisionbyzero'))=>;; function either tests the value and calls functions accordingly=>(eitherprint(fn[x](+x1))(safe-div42))3.0=>(eitherprint(fn[x](+x1))(safe-div10))divisionbyzeroThe identity monad=>(importhymn.dsl[identity-m])=>(requirehymn.dsl[do-monad-return])=>;; do notation with identity monad is like let binding=>(do-monad-return[a(identity-m1)b(identity-m2)](+ab))Identity(3)The lazy monad=>(importhymn.dsl[force])=>(requirehymn.dsl[lazy])=>;; lazy computation implemented as monad=>;; macro lazy creates deferred computation=>(setva(lazy(print"evaluate a")42))=>;; the computation is deferred, notice the value is shown as '_'=>aLazy(_)=>;; evaluate it=>(.evaluatea)evaluatea42=>;; now the value is cached=>aLazy(42)=>;; calling evaluate again will not trigger the computation=>(.evaluatea)42=>(setvb(lazy(print"evaluate b")21))=>bLazy(_)=>;; force evaluate the computation, same as calling .evaluate on the monad=>(forceb)evaluateb21=>;; force on values other than lazy return the value unchanged=>(force42)42=>(requirehymn.dsl[do-monad-return])=>;; do notation with lazy monad=>(setvc(do-monad-return...[x(lazy(print"get x")1)...y(lazy(print"get y")2)]...(+xy)))=>;; the computation is deferred=>cLazy(_)=>;; do it!=>(forcec)getxgety3=>;; again=>(forcec)3The list monad=>(importhymn.dsl[list-m])=>(requirehymn.dsl[do-monad-return])=>;; use list-m contructor to turn sequence into list monad=>(setvxs(list-m(range2)))=>(setvys(list-m(range3)))=>;; do notation with list monad is list comprehension=>(list(do-monad-return[xxsyys:when(not(=0y))](/xy)))[0.00.01.00.5]=>(requirehymn.dsl:readers[@])=>;; @ is the reader macro for list-m=>(list...(do-monad-return...[x#@(range2)...y#@(range3)...:when(not(=0y))]...(/xy)))[0.00.01.00.5]The maybe monad=>(importhymn.dsl[JustNothingmaybe])=>(requirehymn.dsl[do-monad-return])=>;; do notation with maybe monad=>(do-monad-return[a(Just1)b(Just1)](/ab))Just(1.0)=>;; Nothing yields Nothing=>(do-monad-return[aNothingb(Just1)](/ab))Nothing=>;; maybe is a function decorator that wraps return value into maybe=>;; a safe-div with maybe monad=>(importhy.pyops[/])=>(setvsafe-div(maybe/))=>(safe-div4242)Just(1.0)=>(safe-div42'answer)Nothing=>(safe-div420)NothingThe reader monad=>(importhymn.dsl[lookup-reader])=>(requirehymn.dsl[do-monad-return])=>;; do notation with reader monad,=>;; lookup assumes the environment is subscriptable=>(setvr(do-monad-return[a(lookup-reader'a)b(lookup-reader'b)](+ab)))=>;; run reader monad r with environment=>(.runr{'a1'b2})3The state monad=> (import hymn.dsl [lookup-state set-value])
=> (require hymn.dsl [do-monad-return])
=> ;; do notation with state monad,
=> ;; set-value sets the value with key in the state
=> (setv s (do-monad-return [x (lookup-state "a") _ (set-value "b" (+ x 1))] x))
=> ;; run state monad s with initial state
=> (.run s {"a" 1})
#(1 {"a" 1  "b" 2})The writer monad=>(importhymn.dsl[tell])=>(requirehymn.dsl[do-monad-return])=>;; do notation with writer monad=>(do-monad-return[_(tell"hello")_(tell" world")]None)StrWriter((None,'helloworld'))=>;; int is monoid, too=>(.execute(do-monad-return[_(tell1)_(tell2)_(tell3)]None))6Operations on monads=>(importhymn.dsl[lift])=>;; lift promotes function into monad=>(importhy.pyops[+])=>(setvm+(lift+))=>;; lifted function can work on any monad=>;; on the maybe monad=>(importhymn.dsl[JustNothing])=>(m+(Just1)(Just2))Just(3)=>(m+(Just1)Nothing)Nothing=>;; on the either monad=>(importhymn.dsl[LeftRight])=>(m+(Right1)(Right2))Right(3)=>(m+(Left1)(Right2))Left(1)=>;; on the list monad=>(importhymn.dsl[list-m])=>(list(m+(list-m"ab")(list-m"123")))["a1""a2""a3""b1""b2""b3"]=>(list(m+(list-m"+-")(list-m"123")(list-m"xy")))["+1x""+1y""+2x""+2y""+3x""+3y""-1x""-1y""-2x""-2y""-3x""-3y"]=>;; can be used as normal function=>(importfunctools[reduce])=>(reducem+[(Just1)(Just2)(Just3)])Just(6)=>(reducem+[(Just1)Nothing(Just3)])Nothing=>;; <-r is an alias of lookup-reader=>(importhymn.dsl[<-r])=>(requirehymn.dsl:readers[^])=>;; ^ is the reader macro for lift=>(setvp(#^print(<-r'message):end(<-r'end)))=>(.runp{'message"Hello world"'end"!\n"})Helloworld!=>;; pseudo random number - linear congruential generator=>(importhymn.dsl[state-mget-stateset-state])=>(setvrandom...(>>get-state...(fn[s](state-m.unit(%(+(*s69069)1)(**232))))...set-state))=>(.runrandom1234)#(123485231147)=>;; random can be even shorter by using modify=>(importhymn.dsl[modify])=>(setvrandom(modify(fn[s](%(+(*s69069)1)(**232)))))=>(.runrandom1234)#(123485231147)=>;; use replicate to do computation repeatly=>(importhymn.dsl[replicate])=>(.evaluate(replicate5random)42)[422900899279369741621860856091171637142]=>;; sequence on writer monad=>(importhymn.dsl[sequence])=>(importhymn.dsl[tell])=>(.execute(sequence(maptell(range1101))))5050Using Hymn in Python>>>fromhymn.dslimport*>>>sequence(map(tell,range(1,101))).execute()5050>>>msum=lift(sum)>>>msum(sequence(map(maybe(int),"12345")))Just(15)>>>msum(sequence(map(maybe(int),"12345a")))Nothing>>>@failsafe...defsafe_div(a,b):...returna/b...>>>safe_div(1.0,2)Right(0.5)>>>safe_div(1,0)Left(ZeroDivisionError(...))Requirementshy >= 0.27.0For hy version 0.19, please install hymn 0.9For hy version 0.14, please install hymn 0.8For hy version 0.13, please install hymn 0.7.For hy version 0.12, please install hymn 0.6.For hy version 0.11 and earlier, please install hymn 0.5.See Changelog section in documentation for details.InstallationInstall from PyPI:pip install hymnLicenseBSD New, see LICENSE for details.LinksDocumentation:https://hymn.readthedocs.io/Issue Tracker:https://github.com/pyx/hymn/issues/Source Package @ PyPI:https://pypi.python.org/pypi/hymn/Git Repository @ Github:https://github.com/pyx/hymn/Git Repository @ Gitlab:https://gitlab.com/pyx/hymn/