Where communities thrive


  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
People
Activity
    Kai
    @neko-kai
    scalac will generate tt as a prerequisite to call the macro, but then you may just drop the tree on the floor and it won't exist in the code
    Kai
    @neko-kai
    @lbialy Strong LTag is merged will be released tomorrow
    Łukasz Biały
    @lbialy
    not sure that would work as apply signature would still exists, wouldn't it?
    am I to assume Strong LTag behaves the same as TypeTag - ie all type info provided + compile crash on type vars?
    About boopickle external dep: it might be a problematic transitive dep for many projects that use doobie & boopickle together - they will be forced to either upgrade or find compatible version of boopickle
    Kai
    @neko-kai
    @lbialy Yeah, that's the point of strong tags. From what i can see the last breaking change to boopickle was in 2016:
    https://github.com/suzaku-io/boopickle/releases
    https://github.com/suzaku-io/boopickle/blob/master/CHANGES.md
    I think as far dependencies go, it's one of the safest - but of course it's always possible to shade it
    Łukasz Biały
    @lbialy
    sure, just give me a shout which version of fundamentals-reflection to use after you publish it and I will hack on my doobie PR
    Kai
    @neko-kai

    @lbialy
    fundamentals-reflection 0.9.7 is released
    https://github.com/7mind/izumi/releases/tag/v0.9.7

    "io.7mind.izumi" %% “fundamentals-reflection" % "0.9.7"

    Use LTag instead of LTag.Weak for strong tags

    not sure that would work as apply signature would still exists, wouldn't it?

    Yeah, but it wouldn’t be referred anywhere – when a macro is called, it’s result is expanded in place, that apply is useless for runtime bytecode. If you depend on macro artifact in Provided or Optional scope – it won’t be on the classpath, so there should be no way for the native-image to find it.

    Kai
    @neko-kai
    @lbialy Next version will shade boopickle
    Dermot Haughey
    @hderms
    offtopic but how much of a hassle has it been for you guys to use boopickle
    also does anyone have any experience with evaluating the performance of it?
    I would expect it to be faster than https://github.com/RuedigerMoeller/fast-serialization
    Kai
    @neko-kai
    @hderms Hmm, nearly no hassle, it supports sealed traits & case classes by default and uses macros to derive, so it’s fast – it also derives inductively by default from what I can see (only need to define serialisers for top-level structures, not for all nested leaves) – I’d say it has way higher ergonomics than circe tbh
    Dermot Haughey
    @hderms
    @neko-kai that's good to know
    I wish they published performance metrics at a high enough granularity for us to approximate how fast serialization would be
    we want something that's like 200+ MB/s
    Kai
    @neko-kai
    @hderms re: speed, i asked the guy behind jsoniter and he said to use boopickle, I trust him since he benchmarks everything, but didn’t do benchmarks myself tbh :D https://twitter.com/aplokhotnyuk/status/1162487709023313920
    Dermot Haughey
    @hderms
    heh yeah that dude is a benchmark fiend so I trust him
    Martin
    @mrt181_gitlab
    Hi, distage noob user here
    How can I use distage with newtypes?
    I get this error
    Suppressed: izumi.distage.model.exceptions.MissingInstanceException: Instance is not available in the object graph: {type.com.Types::com.Types.Topic::com.Types.Topic.Type|<scala.Nothing..({scala.Any & com.Types::com.Types.Topic::com.Types.Topic.Tag} & {type __Topic__newtype = __Topic__newtype})>}. required by refs: Set({type.com.ApplicationConf::com.ApplicationConf.KafkaConfig})
    @newtype case class Topic(toTuple: (String, Int))
    Martin
    @mrt181_gitlab
    I am using pureconfig to load the config from the file. In this case I have an object in the config event-topic = { _1: "events", _2: 4 } which returns me the topic name and partition count
    Kai
    @neko-kai
    @mrt181_gitlab
    Did you bind it with make, like make[Topic].from(loadConfig(...)) ?
    Kai
    @neko-kai
    @mrt181_gitlab I added a test just in case, - 7mind/izumi@2025104 - the newtype itself should work just fine, as long as it’s in ModuleDef
    Odomontois
    @Odomontois
    Like foundation library without dependencies on ZIO, etc, capable of existing as a community project
    Kai
    @neko-kai
    @Odomontois zio-tf is in plans for some time, just gotta find the time
    fundamentals-bio’s dependency on ZIO is Optional, zio-tf’s will also probably be optional...
    Kai
    @neko-kai
    @lbialy Did you manage to find the time for that doobie native-image PR?
    Vasily Sulatskov
    @redvasily
    Hi, I wonder if there's a way to check if a call to locator.get[MyClass] where a locator is an instance of izumi.distage.model.Locator has all necessary rules to produce an instance of MyClass or not? I think it should be possible as all the necessary information is present in the injector/plan, but I can't seem to find the relevant API.
    Kai
    @neko-kai

    @redvasily
    Yes, there’s a method OrderedPlan#getImports that you can use to see if there are any missing rules.

    val module: ModuleDef
    val plan = Injector().plan(PlannerInput(module))
    val imports = plan.getImports
    if (imports.nonEmpty) { throw new RuntimeException(s“missing: $imports”)}

    There’s also a compile-time API https://izumi.7mind.io/latest/release/doc/distage/other-features.html#compile-time-checks
    And you can call the checker function at runtime: https://github.com/7mind/izumi/blob/develop/distage/distage-roles/src/main/scala/izumi/distage/staticinjector/plugins/macrodefs/StaticPluginCheckerMacro.scala#L130
    The above getImports will not account for internal magic imports, so you may want to call the above checker function instead, or use an extension method unresolvedImports in StaticPluginCheckerMacro

    If you have a Locator that means “production” is finished, you may check if the instance is there with locator.find[MyClass], you’ll need to check the Plan object before it goes to produce (note that produce method will also check imports first so it’ll have the same diagnostic)
    You can put calls to StaticPluginCheckerMacro.check or the StaticPluginChecker macros into unit tests if you want to check wiring without launching your app
    Kai
    @neko-kai
    ^ note the checker is in distage-roles now, not distage-app
    Vasily Sulatskov
    @redvasily
    @neko-kai Thanks. I've been able to do this using OrderedPlanCheck.unresolvedImports. Can you please explain about this: https://izumi.7mind.io/latest/release/doc/distage/other-features.html#compile-time-checks ? How do I use that API if I have a ModuleDef that contains no unresolved imports? Example code there uses PluginDef and ModuleRequirements. Can it be used with just a ModuleDef?
    Kai
    @neko-kai

    @redvasily
    For compile-time API you need to wrap your module value in a plugin:

    final class MyPlugin extends PluginDef {
      include(myModule: ModuleDef)
    }
    StaticPluginChecker.checkWithConfig[MyPlugin, NoModuleRequirements](disableTags = "", configFileRegex = "*.application.conf")

    For runtime check you can convert any Module type into any other Module type with .morph method

    val plugin = myModule.morph[PluginBase]
    Vasily Sulatskov
    @redvasily
    @neko-kai Thank you. Unfortunately I can't get it to work for the moment on my main project. I'll try later on a small test project. Anyway, it's not that big of a deal, runtime check with unresolvedImports seem to work good enough for practical purposes.
    Vasily Sulatskov
    @redvasily

    Hello, I am trying to use AutoFactories, and while I can get them to work for me in some cases, but in some cases they don't really work as I would expect them to work.
    Here's an example: https://github.com/redvasily/distage-named-factories-test/blob/master/src/main/scala/example/Example.scala

    What I am trying to do here is to create an AutoFactory Function0[MyClass] where while MyClass has plenty of dependencies which I don't really have a plan for, but on the other hand, I've added a plan for MyClass itself, using a make[MyClass].from(() => new MyClass(params....))

    I guess this might seem contrived, but I do have something like that in my prod code.

    Kai
    @neko-kai

    @redvasily
    That seems like it works correctly, when seeing a factory for each factory method:

    def apply(): MyClass // MyClass has argument Int

    for each argument of class that ISN’T on the method, the argument is taken from object graph.
    In this case there are no arguments for Int on apply, so Int must be from the ModuleDef:

    class Factory(int: Int) extends (() => MyClass) {
      def apply() = new MyClass(int)
    }

    The make[MyClass] does not change anything and is not referenced

    Vasily Sulatskov
    @redvasily
    @neko-kai I guess what I am looking for is an equivalent of Guice's providers. My real problem is like this: I need to be able to construct instances of KafkaConsumer classes when I need to. And these classes take a java Map<String, Object> configs as an argument. At the same time I have different types of consumers in the application, constructed with different properties:
      make[KafkaConsumer[String, String]].named("transactional").from {
        props: Map[String, AnyRef] @Id("transactional") => new KafkaConsumer[String, String](props.asJava)
      }
      make[KafkaConsumer[String, String]].named("nonTransactional").from {
        props: Map[String, AnyRef] @Id("nonTransactional") => new KafkaConsumer[String, String](props.asJava)
    }
    But in my code I would like to be able to just have a "provider" for KafkaConsumer with these annotations. With Guice that would've been something like this:
    transactionalProvider: Provider[KafkaConsumer[String, String]] @Named("transactional") with provider being created automatically.
    Vasily Sulatskov
    @redvasily
    I think that the equivalent to that in distage is AutoFactories, though I do understand that it's mostly created for assisted injection. But I can't seem to get distage to work for this case. Is there a way to have distage create factories like this:
    make[() => KafkaConsumer[String, String]].named("transactional") // from....
    make[() => KafkaConsumer[String, String]].named("tnonTransactional") // from...
    At the moment I create these factories manually, but it feels like there should be a better way do this.
    Kai
    @neko-kai

    @redvasily
    Aha, I get it now. Distage auto-factories unfortunately aren't equivalent to guice providers since they always use default class constructor, not the constructor specified in DI – we actually ended up never using auto-factories, so didn’t think of such a case.
    There’s no built-in way to summon the known constructor of something, except by manually querying the structure to find it there:

    class MyClass(thisGraph: LocatorRef) {
      val kafkaConstructor = thisGraph.get.plan.collect {
        // only IFF kafka is bound with a lambda as above
        case CallProvider(key, func, _) if key == DIKey.get[KafkaConsumer[String, String]].named(“transactional”) =>
          ProviderMagnet(func.provider)
      }.steps.head
    
      val newConsumer = thisGraph.get.run(kafkaConstructor)
    }

    You may open a GitHub issue or PR for this functionality

    In real code i’d just make a manual factory for this case:

    class KafkaFactory(
      propTransactional: Map[String, AnyRef] @Id(“transactional”),
      propNonTransactional: Map[String, AnyRef] @Id(“nonTransactional”),
    ) {
      def transactional: KafkaConsumer[String, String] = mkConsumer(propTransactional)
      def nonTransactional: KafkaConsumer[String, String] = mkConsumer(propNonTransactional)
    
      private def mkConsumer(props: Map[String, String]): KafkaConsumer[String, String]
    }
    And reuse it in global bindings if i need to:
    make[KafkaConsumer[String, String]].named(“transactional”).from((_: KafkaFactory).transactional)
    make[() => KafkaConsumer[String, String]].named(“transactional”).from((k: KafkaFactory) => () => k.transactional)
    Vasily Sulatskov
    @redvasily
    @neko-kai Thanks for the explanation. I guess not having providers is not that big of a deal, as adding a provider manually when necessary is not that hard. However I am just curious, is it possible automatic provider creation from the information already provided to the plan if possible (CallProvider case or maybe constructor can work as well)?
    Kai
    @neko-kai
    @redvasily Yeah, it is. That’s kind of what PlanInterpreter does, except it just executes the constructors instead of making them available. I think it would be best to modify PlanInterpreter, but you may also do it with a hook or a separate pass. You may for example write an ImportStrategy that for all keys like () => T fills them with a function that calls WiringExecutor#execute on operation of T. And construct a new Injector with a BootstrapModule with your ImportStrategy