Closed MicahZoltu closed 1 year ago
One of the main reason for libraries wasn't upgradeability, but reducing code duplication, which it fulfils. Supporting upgradeable libraries is tracked in #2469.
I don't fully get how Solidity libraries are that different to other libraries considering how a blockchain is a different setting. Could you elaborate a bit more and what naming would you suggest?
TL;DR: After thinking more on the subject I think my issue is with the fact that Solidity libraries are dynamic libraries rather than static and libraries do delegate call instead of call.
In most other languages (exception being C for dynamic libraries), libraries are a build-time dependency, not a runtime dependency. I suspect this is the source of the confusion because libraries in Solidity are runtime dependencies in that it is expected they have previously been uploaded to the blockchain and the developer will provide linking addresses (which is a painful process when you consider that the linking address differs per environment). In general, I loath dynamic libraries in C as over the years I have burned a massive amount of time dealing with the trouble they cause and in general I very much prefer runtime/static linking. Also, almost every modern language has moved away from dynamic linking other than links to the OS due to the amount of pain it causes.
I recognize that runtime linking saves on uploading gas, but in general I believe that any project of substance is more than willing to pay a few dollars extra to upload their project to main net. That being said, I do believe there is value in providing a mechanism by which a multi-contract project can easily have a library that is referenced by multiple contracts in the project. At the moment, one needs to upload the library contract, get the address of it, then construct all of your other contracts with the library's address.
The other issue that makes libraries different from other languages is the fact that they are delegate called. I would prefer it if libraries had static analysis checks that ensured that they never wrote to storage and were called with a normal call (or delegate call as it doesn't really matter). The whole delegate call of libraries results in an unusual pattern similar to extension methods in languages like C# and Kotlin, where one can add methods to a class. However, even in those languages extension methods do not gain access to private storage of the target class, they are only a syntactic sugar around calling a static function with an instance of the target class mapped to this
.
In general, while the library pattern is clever I don't find that it encourages good application design/architecture and leveraging it requires a complicated build/deploy process. I would like to see re-usable code become a first class citizen in Solidity, but I believe in order to be more usable a dependency management system is necessary, libraries should be statically linked, and they shouldn't have private access to the using class's state. With regards to this issue, what Solidity has is a new language design concept that doesn't match what other (modern) languages call a library, which is why I believe the naming to be a poor choice. I would also like the name to be freed up to be used in the future when Solidity has a dependency management solution and more traditional library support (e.g., static classes that one can call functions on and classes that one can easily instantiate and use).
As an example of something I think of as a library contract is https://github.com/AugurProject/augur-core/blob/develop/src/libraries/set.se (sorry, hasn't been migrated to Solidity yet). It has its own storage and it is instantiated by some other class and called with CALL, not DELEGATECALL.
I'll let someone else talk about the delegate call matters, but I want to speak to the matter of "other languages" and to suggest that comparisons of this nature don't really end up clarifying this issue and leading to to a course of action.
I don't think it's really accurate to say that "most languages" treat libraries as "build time dependencies." A significant number of the most popular languages are dynamic and interpreted, and thus there isn't really a "build time" and encountering a library dependency will happen at run time. You previously mentioned C# as your frame of comparison. I haven't done a lot of C# work, but I've been working in Java since 1998, and libraries in Java are not a compile time dependency. It is necessary to have access to class/method/field names and types at compile time, but the linkage is dynamic and you can easily substitute one library for another at run time. Anyone who's worked in Android has experienced this firsthand-- the Android SDK is a type-compliant set of function stubs, and the actual Android framework transparently replaced at run time.
"Library" means different things in different language contexts, so can we perhaps refine this issue into something more actionable?
Sorry this will not be a comprehensive answer.
In most other languages (exception being C for dynamic libraries), libraries are a build-time dependency, not a runtime dependency.
I find the opposite more likely to be true, especially considering market share:
I recognize that runtime linking saves on uploading gas, but in general I believe that any project of substance is more than willing to pay a few dollars extra to upload their project to main net.
This is all fine up to a certain level, until blockchain bloat becomes unbearable and a limiting factor. Hopefully before it becomes a problem there will be means, such as rent cost, to keep only relevant code in the blockchain. At that point this will be a very desired feature. Note at the time Solidity libraries were implemented, blockhain rent was already discussed.
I suspect this is the source of the confusion because libraries in Solidity are runtime dependencies in that it is expected they have previously been uploaded to the blockchain and the developer will provide linking addresses
I think, as you already mentioned elsewhere, the main problem is with tooling instead.
I would prefer it if libraries had static analysis checks that ensured that they never wrote to storage and were called with a normal call (or delegate call as it doesn't really matter).
With #992 and #2606 that will be possible and we're working towards that.
Regardless, I believe one could easily argue that the current implementation is flawed. I still think that Solidity needs to work on a decent standard library with contracts that can be simply "baked in" when imported from said standard library. Library here could just mean "runtime linked immutable functionality"...which is certainly useful and does work to an extent...still is relatively painful and awkward to work into a dev pipeline as @MicahZoltu suggests. I'm not certain if hardcoding addresses to certain libraries on the live network would be a substantially better solution but certainly it might be worth consideration if it is decided that a standard library could be a workable solution?
All the methods marked internal
will be statically linked and a good while back I've suggested an option which turns on static linking (even for those methods not marked internal
). I think that would good a middle ground.
And I've also argued to not use internal
, but inline
as a keyword in libraries and keep internal
for library-internal usage and hidden from the user.
I have realized, through this discussion, (sorry for not having all of my thoughts in order up front) that I really have two separate problems with libraries right now:
@axic @roadriverrail You are correct, I am using the term dynamic
and static
incorrectly here, I apologize. In traditional languages, if I am dynamically linking to a library I need to know the shape of the library, and its lookup key (e.g., name and sometimes version). With that information, the underlying OS/runtime has the ability to find the library for me if it exists, and it will do so whether I bundle the library with my code or if the library pre-existed on the system.
With Solidity, at link time I need to provide the location of the library. This is what makes the build tooling so complicated, because I have to build a separate "binary" for each environment I want my code to run on. If I build/link against main network, my code will not work on a test network.
The other big part of my dislike for libraries is the delegate call thing. In my projects I tend to treat/think of libraries as classes that provide useful value and I can instantiate. However, in Solidity libraries function more like a collection of state-less static methods/extension methods. I cannot, for example, instantiate a library in my contract when I want a list/stack/queue. Also, any libraries I use that have state will mess with the state ordering of of my contract, which is a very unexpected behavior.
I also realize through this discussion that I don't have a concrete proposal for how to fix things. :/ I think libraries in the form of stateless contracts that provide extension methods via using ... for ...
are valuable and useful, but I have yet to find a use for libraries outside of that context, and stateful libraries make my brain melt (I can't grok how exactly they interact with my contract or make sense or where I would use them).
Hi everyone! This issue has been closed due to inactivity. If you think this issue is still relevant in the latest Solidity version and you have something to contribute, feel free to reopen. However, unless the issue is a concrete proposal that can be implemented, we recommend starting a language discussion on the forum instead.
The keyword
library
causes a lot of confusion because alibrary
in Solidity is different from a library in just about any other language. For example, in C# one would call the .NET Framework a library. It is a collection of contracts that can be useful for developing applications.ImmutableList
, for example, is a C# library class that you can instantiate to get an immutable collection with certain properties.In JavaScript, a library is something like Underscore, which is a collection of helpful functions.
In Solidity though,
library
means a particular type of contract that is called in a certain way and must be utilized in a very constrained fashion. I find myself very often in incredibly confusing conversations because I want to talk about a library in the traditional sense (a useful contract/collection of contracts that I can build my application with) but I no longer can use the term library and have to come up with some other term that no one understands. Similarly, when people come to talk to me about libraries they rarely mean Soliditylibrary
, they almost always mean library as used in the rest of the industry.I would like to see
library
in Solidity renamed to something that is more clearly constrained.mild tangent
Personally, I haven't yet found libraries to be particularly useful outside of the
using ... for ...
context because they require a pretty complex build/deploy pipeline to get working smoothly, something which almost no projects have setup. Also, because most people want upgradability (which is why they use delegate calls), libraries (which require baking the library address in at compilation time) don't offer this feature.