I think there's a difference in how the JVM loads classes (dependencies always being available on the classpath) and the CLR. C#'s Type.GetType(): Automatically triggers assembly loading for shared framework assemblies: https://github.com/dotnet/runtime/blob/main/src/coreclr/System.Private.CoreLib/src/System/Reflection/TypeNameResolver.CoreCLR.cs#L190-L207 The flow is:
1. C# Type.GetType() →
2. TypeNameResolver.GetType() →
3. ResolveAssembly() →
4. RuntimeAssembly.InternalLoad() ← This loads from shared frameworks e.g. .NET.Sdk.Web
Clojure's RT.classForName(): Does NOT trigger assembly loading - only searches already-loaded assemblies which makes it hard/unidiomatic to consume/depend on any .NET goodies.
<Project Sdk="Microsoft.NET.Sdk.Web">The only workaround I found is manually calling Assembly.Load() before using Clojure, but:
- You need to know which assemblies to preload
- Goes against .NET's design
- Not discoverable - users hit "Type not found" errors
Also changing Clojure.RT is not backwards compatible I guess(?)
But if we had it, we could add <Project Sdk=" and start using http://ASP.NET Core idiomatically like you can in C# or F#
I'm open to suggestions on how to fix this. Yes, at the moment, manually calling Assembly.Load() before you hit the type of interest in Clojure code is the only way to proceed.
I don't know that changing Clojure.RT is out of the question. Worst case: have a flag. Or have a way to specify the assembly resolver to use that can be set during clojure init.
Looking at System.Reflection.TypeNameResolver.GetType (I wish I had had access to MS source code 15 years ago. Sigh.) One could write a dissertation on this thing.
Let's consider the kind of situation we run into in ClojureCLR: RT.classForName("A.B.T"). We know nothing about the assembly. Where do we look? At the moment, it looks in loaded assemblies. If we are trying to auto-load assemblies, where do we look? What set of directories do we look in? Presumably you would like the .NET system load path? Do we know what defines that? We could certainly cobble up some way for the user to specify other paths to search.
Now as we are trying to find "A.B.T", we will have to look at the internals of every assembly accessible on specified directories. This is not quite as simple as seeing if a Jar has a file named "A.B.T". It requires a full metadata analysis. We do not have to fully load the assembly. There is a provision for a MetadataLoadContext that loads assemblies for metadata inspection only. (https://learn.microsoft.com/en-us/dotnet/standard/assembly/inspect-contents-using-metadataloadcontext)
So I'm thinking auto-loading of an assembly on demand is possible here. The biggest problems: What is the default for the search directories? How does the user specify additional directories?
I'd love to solve this problem.
This looks interesting: Microsoft.Extensions.DependencyModel.DependencyContext.Default; https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.dependencymodel.dependencycontext?view=net-9.0-pp
Here is how ClojureDart does generics (not that it is necessarily better). You might find this interesting @dmiller: https://github.com/Tensegritics/ClojureDart/blob/main/doc/ClojureDart%20Cheatsheet.pdf
Thanks for the reference.