我想使用的状态单子转换Scalaz 7通过一个分析器,线程额外的状态,我无法做任何有用的事情而无需编写大量的t m a -> t m b版本m a -> m b的方法.
假设我有一个包含嵌套括号的字符串,其中包含数字:
val input = "((617)((0)(32)))"
Run Code Online (Sandbox Code Playgroud)
我还有一个新的变量名称流(在这种情况下是字符):
val names = Stream('a' to 'z': _*)
Run Code Online (Sandbox Code Playgroud)
我想从流的顶部拉出一个名称,并在解析它时将其分配给每个括号表达式,然后将该名称映射到表示括号内容的字符串,并将嵌套的括号表达式(如果有)替换为他们的名字.
为了使这更具体,这就是我希望输出看起来像上面的示例输入:
val target = Map(
'a' -> "617",
'b' -> "0",
'c' -> "32",
'd' -> "bc",
'e' -> "ad"
)
Run Code Online (Sandbox Code Playgroud)
在给定级别可能存在一串数字或任意多个子表达式,但这两种内容不会在单个括号表达式中混合.
为了简单起见,我们假设名称流永远不会包含重复项或数字,并且它总是包含足够的输入名称.
上面的示例是此Stack Overflow问题中解析问题的略微简化版本 .我用一个大致如下的解决方案回答了这个问题:
import scala.util.parsing.combinator._
class ParenParser(names: Iterator[Char]) extends RegexParsers {
def paren: Parser[List[(Char, String)]] …Run Code Online (Sandbox Code Playgroud) 假设我有这样的ADT:
sealed trait Event
case class Foo(i: Int) extends Event
case class Bar(s: String) extends Event
case class Baz(c: Char) extends Event
case class Qux(values: List[String]) extends Event
Run Code Online (Sandbox Code Playgroud)
circe中Decoder[Event]实例的默认泛型推导要求输入JSON包含一个包装器对象,该对象指示表示的是哪个case类:
scala> import io.circe.generic.auto._, io.circe.parser.decode, io.circe.syntax._
import io.circe.generic.auto._
import io.circe.parser.decode
import io.circe.syntax._
scala> decode[Event]("""{ "i": 1000 }""")
res0: Either[io.circe.Error,Event] = Left(DecodingFailure(CNil, List()))
scala> decode[Event]("""{ "Foo": { "i": 1000 }}""")
res1: Either[io.circe.Error,Event] = Right(Foo(1000))
scala> (Foo(100): Event).asJson.noSpaces
res2: String = {"Foo":{"i":100}}
Run Code Online (Sandbox Code Playgroud)
这种行为意味着如果两个或多个案例类具有相同的成员名称,我们就不必担心含糊不清,但它并不总是我们想要的 - 有时我们知道展开的编码是明确的,或者我们想要通过指定顺序来消除歧义应该尝试每个案例类,或者我们只是不在乎.
如何在Event没有包装器的情况下对ADT 进行编码和解码(最好不必从头开始编写我的编码器和解码器)?
(这个问题经常出现 - 例如今天早上 …
假设我有这个monadic类:
case class Foo[A](xs: List[A]) {
def map[B](f: A => B) = Foo(xs map f)
def flatMap[B](f: A => Foo[B]) = Foo(xs flatMap f.andThen(_.xs))
def withFilter(p: A => Boolean) = {
println("Filtering!")
Foo(xs filter p)
}
}
Run Code Online (Sandbox Code Playgroud)
以下是2.10.0 REPL会话:
scala> for { (a, b) <- Foo(List(1 -> "x")) } yield a
res0: Foo[Int] = Foo(List(1))
Run Code Online (Sandbox Code Playgroud)
这与2.10.1中的情况相同:
scala> for { (a, b) <- Foo(List(1 -> "x")) } yield a
Filtering!
res0: Foo[Int] = Foo(List(1))
Run Code Online (Sandbox Code Playgroud)
这对我来说是完全出乎意料的,并且在过滤需要额外约束(例如Scalaz \/或EitherT)的情况下会导致特别混乱的错误.
我无法在2.10.1发行说明中 …
也许我错过了一些明显的东西,但我正在尝试清理一个使用Scalaz 7的项目中的一些样板,而我找不到一个看似非常简单且可能有用的特殊拼图.
假设我们有两种类型的双射:
case class Foo(x: Int)
case class Bar(i: Int)
import scalaz._, Scalaz._, BijectionT._
val fb: Foo <@> Bar = bijection[Id, Id, Foo, Bar](
foo => Bar(foo.x),
bar => Foo(bar.i)
)
Run Code Online (Sandbox Code Playgroud)
现在假设我们发现我们需要List[Foo]和之间的双射List[Bar].我们可以轻松编写一个提供此功能的隐式类(实际上我们也可以使它适用于任何仿函数):
implicit class BijectionLifter[A, B](val bij: A <@> B) extends AnyVal {
def liftInto[F[_]: Functor]: F[A] <@> F[B] = bijection[Id, Id, F[A], F[B]](
_ map bij.to,
_ map bij.from
)
}
Run Code Online (Sandbox Code Playgroud)
请注意,这是bimapHaskell的直接翻译Data.Bijection.Scalaz的双射也有一个名为的方法bimap,但它有一个更繁忙的类型,似乎没有以任何明显的方式做我想要的.
现在我们可以写下面的内容:
fb.liftInto[List]
Run Code Online (Sandbox Code Playgroud)
我们得到了我们需要的双射.
我是否遗漏了一些抽象,这使得我可以用Scalaz 7中已经提供的用于双射的函数和实例更清晰地编写它?
我昨晚熬夜试图弄清楚这个无形的问题,如果我不把它从胸前拿走,我担心它会吃掉我的晚上,所以这里就是这样.
在这个最小化版本中,我只是定义了一个类型类,它将递归转换为异构列表:
import shapeless._
trait DeepHLister[R <: HList] extends DepFn1[R] { type Out <: HList }
trait LowPriorityDeepHLister {
type Aux[R <: HList, Out0 <: HList] = DeepHLister[R] { type Out = Out0 }
implicit def headNotCaseClassDeepHLister[H, T <: HList](implicit
dht: DeepHLister[T]
): Aux[H :: T, H :: dht.Out] = new DeepHLister[H :: T] {
type Out = H :: dht.Out
def apply(r: H :: T) = r.head :: dht(r.tail)
}
}
object DeepHLister extends LowPriorityDeepHLister {
implicit …Run Code Online (Sandbox Code Playgroud) 假设我想编写一个带有以下签名的方法:
def parse(input: List[(String, String)]):
ValidationNel[Throwable, List[(Int, Int)]]
Run Code Online (Sandbox Code Playgroud)
对于输入中的每对字符串,它需要验证两个成员都可以解析为整数,并且第一个成员小于第二个.然后它需要返回整数,累积任何出现的错误.
首先,我将定义一个错误类型:
import scalaz._, Scalaz._
case class InvalidSizes(x: Int, y: Int) extends Exception(
s"Error: $x is not smaller than $y!"
)
Run Code Online (Sandbox Code Playgroud)
现在我可以按如下方式实现我的方法:
def checkParses(p: (String, String)):
ValidationNel[NumberFormatException, (Int, Int)] =
p.bitraverse[
({ type L[x] = ValidationNel[NumberFormatException, x] })#L, Int, Int
](
_.parseInt.toValidationNel,
_.parseInt.toValidationNel
)
def checkValues(p: (Int, Int)): Validation[InvalidSizes, (Int, Int)] =
if (p._1 >= p._2) InvalidSizes(p._1, p._2).failure else p.success
def parse(input: List[(String, String)]):
ValidationNel[Throwable, List[(Int, Int)]] = input.traverseU(p =>
checkParses(p).fold(_.failure, checkValues …Run Code Online (Sandbox Code Playgroud) 假设我有一些Scala代码调用一个_用作标识符的Java库(我做了 - 这是一个很长的故事).这是一个简化的例子:
public class StupidUnderscore {
public static String _() { return "Please give me a real name!"; }
}
Run Code Online (Sandbox Code Playgroud)
没问题,对吧?逃避吧:
scala> StupidUnderscore.`_`
res0: String = Please give me a real name!
Run Code Online (Sandbox Code Playgroud)
这一直有效,直到我今天早上尝试更新到Scala 2.10.2:
scala> StupidUnderscore.`_`
<console>:1: error: wildcard invalid as backquoted identifier
StupidUnderscore.`_`
^
Run Code Online (Sandbox Code Playgroud)
这是由于2.10.1中出现的更改并修复了此问题.从提交消息:
禁止
_作为标识符,它只能带来不良.
嗯,当然,但我不知道为什么这意味着我无法摆脱它 - 我认为这是反引号的意思.
我是否必须编写Java包装器才能使其工作?还有其他方法可以_在Scala中引用Java库的方法吗?
作为脚注,我已经确认可以在不编写任何新Java的情况下解决此问题:
object AwfulMacroHack {
import scala.language.experimental.macros
import scala.reflect.macros.Context
def _impl(c: Context) = {
import c.universe._
c.Expr[String](
Select(Ident(newTermName("StupidUnderscore")), newTermName("_")) …Run Code Online (Sandbox Code Playgroud) 我有以下Scala(2.8)代码:
object A {
val message = "hello"
object B {
val message = "world"
}
}
Run Code Online (Sandbox Code Playgroud)
和类似的Java类:
public class C {
public static String message() {
return "HELLO";
}
public static class D {
public static String message() {
return "WORLD";
}
}
}
Run Code Online (Sandbox Code Playgroud)
当我从Scala调用它们时,这些工作正如我所期望的那样:
scala> A.message
res0: java.lang.String = hello
scala> A.B.message
res1: java.lang.String = world
scala> C.message
res2: java.lang.String = HELLO
scala> C.D.message
res3: java.lang.String = WORLD
Run Code Online (Sandbox Code Playgroud)
但是当我尝试类似Java的东西时,编译器不喜欢第二行:
System.out.println(A.message());
System.out.println(A.B.message()); // cannot find ... symbol : variable …Run Code Online (Sandbox Code Playgroud) 假设我有一个简单的类型类,其实例将为我提供某种类型的值:
trait GiveMeJustA[X] { def apply(): X }
Run Code Online (Sandbox Code Playgroud)
我有一些例子:
case class Foo(s: String)
case class Bar(i: Int)
implicit object GiveMeJustAFoo extends GiveMeJustA[Foo] {
def apply() = Foo("foo")
}
implicit object GiveMeJustABar extends GiveMeJustA[Bar] {
def apply() = Bar(13)
}
Run Code Online (Sandbox Code Playgroud)
现在我有一个类似(但不相关)的类型类,它做同样的事情,但在其类型参数中是协变的:
trait GiveMeA[+X] { def apply(): X }
Run Code Online (Sandbox Code Playgroud)
在它的伴随对象中,我们告诉编译器如何从非协变类型类的实例创建实例:
object GiveMeA {
implicit def fromGiveMeJustA[X](implicit giveMe: GiveMeJustA[X]): GiveMeA[X] =
new GiveMeA[X] { def apply() = giveMe() }
}
Run Code Online (Sandbox Code Playgroud)
现在我希望implicitly[GiveMeA[Foo]]编译得很好,因为只有一种方法可以获得GiveMeA[Foo]我们在这里得到的部分.但它没有(至少不在2.10.4或2.11.2):
scala> implicitly[GiveMeA[Foo]]
<console>:16: this.GiveMeA.fromGiveMeJustA is not a valid …Run Code Online (Sandbox Code Playgroud) 假设我想在一些字符串和整数标识符之间进行映射,并且我希望我的类型使得无法获得运行时失败,因为有人试图查找超出范围的id.这是一个简单的API:
trait Vocab {
def getId(value: String): Option[Int]
def getValue(id: Int): Option[String]
}
Run Code Online (Sandbox Code Playgroud)
但是,如果用户通常会从中获取ID getId并因此知道它们是有效的,那么这很烦人.以下是这种意义上的改进:
trait Vocab[Id] {
def getId(value: String): Option[Id]
def getValue(id: Id): String
}
Run Code Online (Sandbox Code Playgroud)
现在我们可以这样:
class TagId private(val value: Int) extends AnyVal
object TagId {
val tagCount: Int = 100
def fromInt(id: Int): Option[TagId] =
if (id >= 0 && id < tagCount) Some(new TagId(id)) else None
}
Run Code Online (Sandbox Code Playgroud)
然后我们的用户可以使用Vocab[TagId]而不必担心getValue在典型情况下检查查找是否失败,但如果需要,他们仍然可以查找任意整数.但是,它仍然很尴尬,因为我们必须为每种我们想要词汇表的东西编写一个单独的类型.
我们也可以用精致的方式做这样的事情:
import eu.timepit.refined.api.Refined
import eu.timepit.refined.numeric.Interval.ClosedOpen
import shapeless.Witness
class Vocab(values: Vector[String]) { …Run Code Online (Sandbox Code Playgroud)