-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: scala 3 derivation #8
base: master
Are you sure you want to change the base?
Conversation
@@ -9,12 +9,12 @@ trait Codec[A] { | |||
defaults: Codec.Defaults, | |||
index: Int | |||
): Either[Parse.Error, A] | |||
private[toml] def optional: Boolean = false |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This field makes it easier to handle default value for missing field of type Option[?]
. toml does not have the concept of null
or nil
, so we cannot simply convert null into null-value-like AST node.
case None => | ||
Right( | ||
d.defaultParams.get(witnessName) | ||
.map(_.asInstanceOf[t]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
defaultParams is Map[Strng, Any]
, so you need asInstanceOf
.left.map((a,m) => (witnessName +: a, m)) | ||
case None => | ||
d.defaultParams.get(witnessName) match | ||
case None if codec.optional => Right(None.asInstanceOf[t]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
optional returns true
if and only if codec is of type Codec[Option[?]]
, so it is safe to use None.asInstanceOf[t]
where t
is of type Option[?]
.
case Right(t) => ((Value.Tbl(map - head),(tail,0),(path,e)), Some(t)) | ||
case Left((path,e)) => ((value, (labels,index), (head :: path, e)), None) | ||
case Value.Tbl(map) => | ||
map.keySet.diff(labelling.elemLabels.toSet).headOption match |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Scala 2 derivation does not allow redundant fields for type, so I follow that rule.
): DerivedProductCodec[P] with | ||
def apply(value: Value, defaults: Defaults, index: Int): Either[Parse.Error, P] = | ||
type Acc = (Value, (/*labels: */Seq[String], Index), Parse.Error) | ||
inst.unfold[Acc]((value, (labelling.elemLabels, index), (Nil, "")))( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It uses unfold
instead of constructA
because Scala 2 derivation performs stateful operation on labels and index.
d.defaultParams.get(head) match | ||
case None => | ||
if codec.optional then | ||
((value,(tail, index),(path,e)), Some(None.asInstanceOf[t])) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can use None
as default value for field of type Option[t]
.
@keynmol Hello, I guess you're the maintainer of this project. Would you mind if I ask you to review this PR? |
Hi @i10416! Sorry for slow response on this, blame holiday season. I will look at this PR in the next couple of weeks, thanks for taking a stab at this! I have my own version somewhere as well, so I'll compare issues and see how they can be merged to achieve perfection. |
I see. Have a nice holiday season! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In general, this works, good job!
I added a few comments:
- one method needs to be extensively refactored, very difficult to follow
- as many things as possible should be made
private[toml]
(macros often prevent hiding some details, but hopefully various traits can be hidden here) - the project doesn't yet have a
.scalafmt.conf
, to avoid reformatting everything at once (I will do it after your PR), would you mind formatting just the larger files you added in the PR using this config?
version = "3.8.3"
runner.dialect = scala213source3
fileOverride {
"glob:**.sbt" {
runner.dialect = scala212source3
}
"glob:**/project/**.*" {
runner.dialect = scala212source3
}
"glob:**/scala-3/**/*.scala" {
runner.dialect = scala3
rewrite.scala3.insertEndMarkerMinLines = 10
rewrite.scala3.removeOptionalBraces = true
rewrite.scala3.convertToNewSyntax = true
}
}
This will be the config that I will use for the rest of the project
using inst: K0.ProductInstances[Codec, P],labelling: Labelling[P], d:DefaultParams[P] | ||
): DerivedProductCodec[P] with | ||
def apply(value: Value, defaults: Defaults, index: Int): Either[Parse.Error, P] = | ||
type Acc = (Value, (/*labels: */Seq[String], Index), Parse.Error) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think using a tuple here makes things more confusing than they have to be – instead, let's use a case class with meaningful field names and refer to them directly, instead of destructuring with @unchecked
.
At the moment the logic is very different to review because of the amount of tuples
package derivation | ||
import shapeless3.deriving.* | ||
|
||
trait DerivedSyntax {self: Codec.type => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this can be made private[toml]
private[toml] object macros: | ||
inline def defaultParams[T]: Map[String, Any] = ${ defaultParmasImpl[T] } | ||
|
||
def defaultParmasImpl[T](using quotes: Quotes, tpe: Type[T]): Expr[Map[String, Any]] = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo - Parmas instead of Params
seems like the typo is copied from https://github.com/lampepfl/dotty-macro-examples/blob/main/defaultParamsInference/src/macro.scala
package toml | ||
package derivation | ||
|
||
trait DerivedSyntax {self: Codec.type => } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can be made private[toml]
|
||
def parse(toml: String, extensions: Set[Extension] = Set()): Either[Parse.Error, Value.Tbl] | ||
|
||
trait TomlVersionSpecific { self: Toml.type => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
may be this can be made private[toml]
?
close #5
This PR adds Scala 3 derivation.
It pass all the test except one which uses
Map
, that does not seem to haveK0.ProductInstances
.See core/src/test/scala-3/toml/CodecSpec.scala.