Giter Club home page Giter Club logo

contextual's People

Contributors

gitter-badger avatar hagl avatar jd557 avatar kubukoz avatar mielientiev avatar mkotsbak avatar propensive avatar tues avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

contextual's Issues

Provide access to `treatEscapes` through `Prefix`

If I understand correctly, currently all compile time string interpolators defined using Contextual do not "treat" escapes. It seems to me like a fairly common use case to want the standard scala string escaping in one's compile time string interpolator.

Perhaps just an additional Boolean parameter to the apply method of the Prefix Object would suffice. Implementation should not be too difficult as the String => String function is exposed here in the std scala library.

Improve error message for substitutions with wrong type

The error message for a substitution with a wrong type is very cryptic:

import contextual.examples._
import shell._
val x=0
sh"$x"

will give the following compile error:

<console>:19: error: type mismatch;
 found   : Int
 required: contextual.Interpolator.Embedded[String,_1.interpolator.type] where val _1: contextual.Prefix[contextual.examples.shell.ShellInterpolator.Output,contextual.examples.shell.ShellInterpolator.ContextType,contextual.examples.shell.ShellInterpolator.type]
       sh"$x"
           ^

For a user of the contextual library this message can be very difficult to understand and it only indirectly gives a hint that the substituted value should be of type String.
Is it possible to create a better message in this case?

UnsafeVerifier

I use this a lot, might be useful to you

abstract class UnsafeVerifier[A] extends Verifier[A] {
  override def check(s: String): Either[(Int, String), A] =
    Try(attempt(s)).toEither.left.map(_ => (0, fail))

  def attempt(s: String): A
  def fail: String
}

It's not really "unsafe" because it is called at compiletime.

Represent substitution types with an `enum`

Substitution types are currently just Strings. This is convenient in some ways because it's easy to lift a singleton string literal type to its string value, but the number of strings is infinite, and overrepresent the possible kinds of substitution that can be made (which is typically about two or three). There are also no guardrails to help implementors of given Substitutions specify the correct type.

Outdated docs

The first example given on this page is out of date: https://propensive.com/opensource/contextual/#a-simple-example

Specifically, the line type Out = Url should be type Output = Url now. Also, that code block doesn't compile when you paste it into a file (for starters, it's missing a curly brace). It would be nice if you updated the docs to reflect the current code base and make the example work out of the box.

Check version 2.0.0 works correctly

I have migrated the macro from Scala 2.12 to Scala 2.13, and cleaned some things up in the process. Notably, the Prefix has been removed, and the instructions on binding the macro to an interpolator have changed. (The documentation and examples should reflect this.)

In the process, however, two of the tests failed to pass, with mysterious errors. While most extractors seem to work, this release may be useful for existing users who want to migrate to Scala 2.13, but it includes a regression over the previous release, and should not be considered bug-free.

This issue is a reminder that more work needs to be done to investigate this problem.

Support extractor interpolators

Contextual should support compile-time-checked extractor interpolations, too, for example

myJson match {
  case json"""{ "key": $value }""" => value
}

It should even be possible to offer typed extraction by specifying a type ascription, like so:

myJson match {
  case json"""{ "key": ${value: Int} }""" => value
}

Embedding custom types not working with version 2.0.0

Thank you for contextual, it has served me well! I would like to make my interpolator available for Scala 2.13, but contextual 2.0.0 isn't working for me. I have a string interpolator for SPARQL queries, which embeds various types into SPARQL query strings with appropriate formatting. I upgraded to 2.0.0, and now I can't get any of my tests for interpolating various types to compile (phenoscape/sparql-utils#26).

There is a strong possibility I'm doing something wrong, but it has been working well for me with the previous version. I thought this might be related to #61.

Basically the only thing I changed in the upgrade is this line: https://github.com/phenoscape/sparql-utils/blob/7a58aec65caf1d296a01a2ba097b7d88411abdaa/modules/core/src/main/scala/org/phenoscape/sparql/SPARQLInterpolator.scala#L125

Offer better error message when failing to substitute `Nothing`

When the type of an expression that's substituted into an interpolated string is Nothing, it probably means that the value is erroneous, and a message has already been emitted. If we can detect that the value is Nothing as the result of an error, then we don't need to emit another message, since doing so distracts from the "real" error. But if the user really does try to substitute Nothing into an interpolated string, we should emit an error.

If it's impossible to distinguish between Nothing and and error, then we should update the error message to indicate that it's probably because of another error.

allow running a validation phase with a compiletime type

I have the usecase where we are using contextual to generate "keys" for a nested logging framework. We are using contextual to ensure that the keys are alphanumeric.

We also have generic derivation of ADTs, e.g. to generate the keys from the case class fields.

However, although we have the fields of the case classes, we can't currently run the contextualize validation phase on those keys so we are falling back to hacks 😭 (e.g. runtime exceptions)

It would be good to be able to pass a Name <: Symbol through a contextualize validation phase so we can get a compilation failure if a field that is invalid. No interpolation is needed, it's just a String that we're validating.

Offer type refinements on return types

The return type of any Contextual value is fixed, but since the macro is transparent, it could be refined to a subtype. For example, in Guillotine the return type of a shell command such as sh"cat foo" could be typed as Command["cat"] instead of just Command. In this example, it would provide some further opportunities to have type inference automatically choose suitable return types for execution.

Known literal Text types from Gossamer could be refined to have the underlying String literal type available as a member.

This would probably be best provided through a mixin trait along the lines of:

trait Precision[State, Return]:
  interpolator: Interpolator[?, State, Return] =>
    type Refined[S <: String & Singleton] <: Return
    def result(state: State): String & Singleton

A user's implementation of result would provide the String based on the final State value which would be passed into the Refined type constructor to produce a new subtype of Return.

An alternative approach may be to change the Return type to Return[S <: String & Singleton] in all cases, and to provide a default implementation of result for users to override if they want this feature.

Error in example?

Am I using it wrong or is it an error in the example?:

scala> bin"10111000"
<console>:35: error: type mismatch;
 found   : Array[Byte]
 required: contextual.examples.binary.BinParser.Output
       bin"10111000"

Standard collection of interpolators?

Scala and Java both have lots of types that are initialised from a string, where an illegal string will compile but fail at runtime. It'd be great if Contextual could provide standard modules for such types.

I've often felt the need for such a tool for:

I'm sure there are a lot commonly used types out there that would benefit from a literal syntax.

Happy to lend a hand or even write most of these, but if you feel this is a good idea, the structure needs to come from you - do you want that to be a completely separate library that depends on Contextual? Standard Contextual modules that live in this repository?

Don't depend on scala-compiler

scala-compiler shouldn't be a compile dependency for a macro library. You probably meant to mark it (and, possibly, scala-reflect) as Provided, which is what circe does.

support custom StringContext

contextual is currently hardcoded to require the actual scala.StringContext instead of a generating code that expects certain methods to exist.

This is problematic, since it means the user cannot provide a custom StringContext to (for example) disable or change the s interpolator.

How does type embedding work?

I really like to use your lib to do syntax checks on SQL/HQL queries. However, my queries are not pure strings, but rather interpolated strings with parameters, e.g.:

val id = 1
val query = q"from User where id = $id"

Would that be possible with contextual? Is there any working example for such an interpolated string with arguments? So far I could only find the section Embedded Types on https://propensive.com/opensource/contextual/#a-simple-example, but that's only very few information about how this works, and the example is unfortunately incomplete. A complete example would already help a lot.

Avoid runtime reflection when instantiating contexts

Lines 200-212 of interpolator.scala use forName and getField to access the name of the context by its string name. This could be avoided by just constructing a Select tree at compile time, and referring directly to the object.

Error macro has not been expanded in Scala 2.13.0

package com.mobimeo.ticketissuer.primitives

import contextual._

object Test  {
  val NonEmptyString = NonEmptyStringModule.Instance
  type NonEmptyString = NonEmptyString.Type

  sealed abstract class NonEmptyStringModule {
    type Type <: String

    def apply(string: String): Option[NonEmptyString] =
      NonEmptyStringParser.check(string).toOption
  }

  object NonEmptyStringModule {
    val Instance: NonEmptyStringModule = new NonEmptyStringModule {
      type Type = String
    }
  }

  object NonEmptyStringParser extends Verifier[NonEmptyString] {
    def check(string: String): Either[(Int, String), NonEmptyString] =
      if (string.isEmpty()) Left(0 -> "string must not be empty") else Right(string.asInstanceOf[NonEmptyString])
  }

  implicit class NonEmptyStringContext(sc: StringContext) {
    val n = Prefix(NonEmptyStringParser, sc)
  }

  n"foo"

}

This gives me an Error:(31, 3) macro has not been expanded n"foo"

It did work in Scala 2.12.x

Support for generic interpolators

This looked promising:

class SqlInterpolator[T[_[_]]:BirecursiveT] {
  object Expr extends StaticInterpolator[T[Sql]] {
    def parse(s: String): String \/ T[Sql] =
      parser[T].parseExpr(Query(s)).leftMap(parseError => s"Not a valid SQL expression: $parseError")
  }
}

along with:

implicit class SqlStringContext(sc: StringContext) {
  def sqlE[T[_[_]]: BirecursiveT] = Prefix((new SqlInterpolator[T]).Expr, sc)
  def sqlB[T[_[_]]: BirecursiveT] = Prefix((new SqlInterpolator[T]).Blob, sc)
}

This particular example is attempting to define a String interpolator for a SQL AST defined in fix point style (for use with matryoshka) but really I think it illustrates the inability to make any kind of generic interpolator using contextual which is unfortunate because this is supported by Scala despite certain weird behaviors surrounding supplying a type parameter: https://stackoverflow.com/questions/44663377/is-it-possible-to-specify-type-parameters-for-string-interpolation-in-scala
This is the error that is generated at compile time with the above example:

java.lang.ClassNotFoundException: quasar.sql.SQLSpec.<local SQLSpec>.query.interpolator.type$

Presumably, caused by the macro implementation having trouble accessing the object inside the class. Maybe it's fundamentally impossible to get this to work in which case I think it should be clearly documented.
This is the definition of StaticInterpolator not that it matters all that much to demonstrate the above point (as a side note this is pretty generic and could be added to contextual itself minus the use of scalaz):

trait StaticInterpolator[A] extends Interpolator {
  def parse(s: String): String \/ A

  def contextualize(interpolation: StaticInterpolation) = {
    interpolation.parts.foreach {
      case lit@Literal(_, string) =>
        parse(string) match {
          case -\/(msg) => interpolation.abort(lit, 0, msg)
          case _        => ()
        }

      case hole@Hole(_, _) =>
        interpolation.abort(hole, "substitutions are not supported")
    }

    Nil
  }

  // A String that would not parse should not have made it past compile time
  def evaluate(interpolation: RuntimeInterpolation): A =
    parse(interpolation.parts.mkString).valueOr(errMsg =>
      scala.sys.error(s"Something terribly wrong happened. Failed to parse something at runtime that we checked at compile time. Reason: $errMsg. This is either because the `parse` function of this `StaticInterpolator` is not pure or could (less likely) be a bug with `StaticInterpolator`"))
}

'scala.reflect.macros.whitebox.Context' is missing from the classpath

import com.sksamuel.scrimage.Image

object RscInterpolator extends contextual.Interpolator {
  override type Input = String
  override type Output = Image

  override def contextualize(interpolation: StaticInterpolation): Seq[ContextType] = {
    val head = interpolation.literals.head
    if (interpolation.macroContext.classPath.map(_.toString).contains(head)) Nil
    else interpolation.abort(Literal(0, head), 0, "Can't find image on classpath.")
  }

  def evaluate(interpolation: RuntimeInterpolation): Output =
    Image.fromResource(interpolation.literals.head)
}

the above code is intended to check at compile time for existence of files on class path. however, it doesn't compile for unclear reason:

[error] /Users/crimson/IdeaProjects/wmh-randomizer/macros/src/com/crimzie/wmh/util/RscInterpolator.scala:12:9: Symbol 'type scala.reflect.macros.whitebox.Context' is missing from the classpath.
[error] This symbol is required by 'value contextual.Interpolator.StaticInterpolation.macroContext'.

regex with a linter

this is basically nerd sniping of the highest order, but wouldn't it be nice to have a regex that linted its contents at compile time? I'm not aware of any such linter but surely there has got to be one.

contextual version 3.0.0 prints interpolated text during compile

Very minor issue, but I think there may be some debugging statements left in. For example, with my interpolator:

scala> import org.phenoscape.sparql.SPARQLInterpolation._
import org.phenoscape.sparql.SPARQLInterpolation._

scala> val s = sparql"hello"
Literals: 'List(hello)'
s: org.phenoscape.sparql.SPARQLInterpolation.QueryText = QueryText(hello)

I don't expect to see Literals: 'List(hello)'.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.