T, but all fields are wrapped by F ),%<5 '>σʔλܕ5ͷશϑΟʔϧυΛ'Ͱϥοϓͨ͠ܕ case class UserProfile(name: String, age: Int) val hkd = HKD[UserProfile, Option](Some("Name"), None) val name: Option[String] = hkd.name val age: Option[Int] = hkd.age
T, but all fields are wrapped by F ྫ͑),%<6TFS1SPGJMF 0QUJPO> 6TFS1SPG0QUͷΑ͏ͳܕͱΈͳͤΔ case class UserProfile(name: String, age: Int) val hkd = HKD[UserProfile, Option](Some("Name"), None) val name: Option[String] = hkd.name val age: Option[Int] = hkd.age case class UserProfOpt(name: Option[String], age: Option[String])
T, but all fields are wrapped by F ͪΌΜͱϑΟʔϧυ໊ͰΞΫηεͰ͖Δ ܕ͍͍ͭͯΔ case class UserProfile(name: String, age: Int) val hkd = HKD[UserProfile, Option](Some("Name"), None) val name: Option[String] = hkd.name val age: Option[Int] = hkd.age case class UserProfOpt(name: Option[String], age: Option[String]) we can safely access fields by name
T, but all fields are wrapped by F ΠϯελϯεੜͰ໊લ͖Ҿ͑Δ case class UserProfile(name: String, age: Int) val hkd = HKD[UserProfile, Option]( name = Some("Name"), age = None ) val name: Option[String] = hkd.name val age: Option[Int] = hkd.age named arguments are also available
should be an instance of InvariantMonoidal GPMEϝιουʹΑΓ),%<5 '>͔Β'<5>͕ಘΒΕΔ ͨͩ͠'͕*OWBSJBOU.POPJEBMͰ͋Δඞཁ͕͋Δ val hkd = HKD[UserProfile, Option]( name = Some("Name"), age = Some(50) ) val profileOpt: Option[UserProfile] = hkd.fold println(profileOpt) // Some(UserProfile("Name", 50))
Applicative composition! u And it also can be used for contravariant version: Divisible ),%"QQMJDBUJWFͷ߹ʹར༻Ͱ͖Δ ίϯτϥόϦΞϯτ൛ͷ%JWJTJCMFͰར༻Մೳ def validate(in: FormValue): Validated[UserProfile] = { HKD[UserProfile, Validated]( name = validateName(), age = validateAge() ).fold }
with all fields wrapped by Option .JSSPSͱ5VQMF.BQΛΈ߹ΘͤΔͱ શͯͷϑΟʔϧυΛ0QUJPOͰϥοϓͨ͠ܕ͕ಘΒΕΔ case class UserProfile(name: String, age: Int) /* Tuple.Map[m.MirroredElemTypes, Option] * = Tuple.Map[(String, Int), Option] * = (Option[String], Option[Int]) */ val m = summon[Mirror.ProductOf[UserProfile]] val mapped: Tuple.Map[m.MirroredElemTypes, Option] = (Some("Name"), None)
by F .JSSPSͱ5VQMF.BQΛ྆ํอ࣋͢ΔͷΛ ),%ͱͯ͠ఆٛ͢Δ class HKD[T <: Product, F[_]] (m: Mirror.ProductOf[T]) (tuple: Tuple.Map[m.MirroredElemTypes, F]) val hkd: HKD[UserProfile, Option] = new HKD(summon[Mirror.ProductOf[UserProfile]]) (Some("Name"), None)
field of HKD by field name u And static type check! ϑΟʔϧυ໊Ͱͷܕ҆શͳΞΫηε Ͳ͏࣮ͬͯݱ͢Δʁ case class UserProfile(name: String, age: Int) val hkd = HKD[UserProfile, Option](Some("Name"), None) val name: Option[String] = hkd.name val age: Option[Int] = hkd.age
Dynamic, obj.foo is translated to the method call obj.selectDynamic("foo") l Contrary to its name, the method call statically checks the type u Mirror provides field names as tuple of literal types %ZOBNJDΛར༻͢Ε࣮Ͱ͖Δʂ %ZOBNJD໊લʹͯ͠੩తʹݕࠪ͞ΕΔ val name: Option[String] = hkd.name // is equivalent to val name: Option[String] = hkd.selectDynamic("name") The static type is decidable because the argument has a literal type "name"
a constructor? Πϯελϯεੜ࣌ͷ໊લ͖Ҿ Ͳ͏࣮ͬͯݱ͢Δʁ case class UserProfile(name: String, age: Int) val hkd = HKD[UserProfile, Option]( name = Some("Name"), age = None )
u HKD(a = x, b = y) à HKD.applyDynamicNamed("apply")(("a",x),("b",y)) Ҿͷܕ͜Μͳײ͡ͷܕʹͳΔ ෳҾ BVUPUVQMJOH ͰλϓϧԽ͞ΕΔ Tuple.Map[m.MirroredElemTypes, F] parameter auto tupling Tuple.Zip[m.MirroredElemLabels, Tuple.Map[m.MirroredElemTypes, F]] parameter auto tupling
wrapped by F u HKTree provides intuitive constructor syntax like HKD l named arguments are available u hmap and fold are also available l fold requires F is an instance of Applicative ߏͷશͯͷ༿͕'Ͱϥοϓ͞Εͨܕ),5SFF ͜Ε࠶ؼత),%Ͱɺ),%ͱಉ༷ͷϝιουΛ࣮Մೳ F[A] F[B] F[C]
later by hmap and fold! ),5SFFΛͬͯཧͷߏͱηϚϯςΟΫεΛ͢Δ ηϚϯςΟΫεINBQͱGPMEͰޙ͔ΒೖͰ͖Δ val hktree = HKStruct[User, [t]=>>Either[String,t]]( id = Left("user id should be positive"), profile = HKStruct[UserProfile, [t]=>>Either[String,t]]( name = Right("Name"), age = Left("age should be smaller than 1000") ) ) val validated = hktree.hmap(toValidated).fold build logic structure run on the semantics of Validated
relationship l HKTree[Sub, F] <: HKTree[Super, F] l covariant u HKForest: Encode as a branching l HKForest[Super,F] = HKForest[Sub1,F] +...+ HKForest[Subn,F] l invariant / F should support sum operation ܕͷѻ͍ํ௨Γ͋ΔɻαϒλΠϓͱΈͳ࣮ͨ͠ ͕),5SFF ࢬ͔ΕͱΈͳ࣮͕ͨ͠),'PSFTU
covariant and contravariant semantics! *OWBSJBOUͳͷͰɺ$PWBSJBOU$POUSBWBSJBOUͷ ͲͪΒͷηϚϯςΟΫεͰೖՄೳ val syntax: HKForest[UserProfile, Syntax] = { word("UserProfile(") *>: HKProduct[UserProfile, Syntax]( name = word("name: ") *>: string :<* word(", "), age = word("age: ") *>: int ) :<* word(")") } val printer: Printer[UserProfile] = syntax.hmap(syntaxToPrinter).fold val parser: Parser[UserProfile] = syntax.hmap(syntaxToParser).fold invariant logic structure We can share the same logic structure between parser and printer!
an instance of InvariantSemiringal l InvariantMonoidal + sum operation u F must be an instance of Defer l To handle recursive data without infinite recursion GPMEϝιουΛར༻͢Δʹ '͕*OWBSJBOU4FNJSJOHBM͔ͭ%FGFSͰ͋Δඞཁ͕͋Δ
Applicative composition l Mirror (isomorphism) + Tuple.Map (apply F) + Dynamic (intuitive API) u Recursive HKD: HKTree, HKForest l They enable us to separate logic structure and semantics l We can inject both Alternative and Decidable semantics for HKForest .JSSPS 5VQMF.BQ %ZOBNJDͰ),%͕࡞ΕΔ ࠶ؼత),%ߏͱηϚϯςΟΫεͷʹཱͭ repo: https://github.com/phenan/hkdms