> this falls for the same kind of over-engineering which caused log4j's issues in the first place
Because it uses an effects system, which are designed to control side effects like the one which caused log4j's vulnerability, it looks like a reaction against log4j, not "more of the same problem".
Whether this is a sound approach or not, I cannot say. But definitely not a case of log4j-like overengineering.
Sadly the effect system isn't granular to narrow down what effect is happening other than "IO", which could be logging, network, disk access, rm -rf /. And without that visibility it doesn't provide you much advantage.
Haskell similarly suffers, I'd like to see IO broken down into composable units of functionality so I can see if e.g. my logging library has some ridiculous network constraint.
I suspect the same re: this library: IO is indeed too broad, and a logging system needs to do IO.
You could use other monads though.
> Haskell similarly suffers
Haskell practitioners aim to write as much code as possible outside the IO monad. If everything you write lives inside that monad, what's the benefit?
The nice thing about Haskell is that if you write every function with IO in its signature, it's bound to raise eyebrows in code reviews, whereas in most other mainstream languages everything lives in an IO monad by default!
I think the point was that for that remaining bit that can’t be extracted into pure functions still is really broad in what it can do with just “IO”
IO on it’s own could mean disk, network, environment variables, sub processes, etc.
My take away is that they’d like to see IO defined as the union of its parts that can also be decomposed, so you’d have a DiskIO monad or ExecIO
That way you can be more certain that you’re not accidentally going to do a bunch of network io before your sub process kicks off. Right now you can’t have that guarantee (out of the box, at least. idk if there’re any user land libraries to do such a thing)
Having been primarily a Scala dev for the last 12 years I feel like people have slowly lost their minds over what pragmatic FP in Scala should be.
People are willingly recreating the Java situation of 'I don't know the project yet but I know we need Spring!' with these ridiculous libraries from Haskell zealots for no obvious benefit.
The ergonomics of this lib out of the box are literally worse for no reason, and worse than most other existing logging libs, because of it
As an observer of this exact phenomenon, I suggest that it is due to the culture that comes with scala. In any given scala-centric organization, there's going to eventually be someone who has mastered the art of talking High Scala. Unless there is someone equally skilled on both dimensions (persuasion and SW eng), and unfortunately, someone approximately as senior, then there is an interative game where added abstraction and complexity become the primary way to distinguish yourself as a "real" engineer.
This happens outside of scala shops, and I've seen it there, too, especially in C++ shops, but scala seems to attract these people.
Erik Meijer, one of the early practitioners and contributors of Scala, and a coauthor of Odersky's Coursera courses on Scala, believes that
"Mostly Functional Programming Doesn't Work" [1]
Note it's "mostly functional" that doesn't work, not functional. He believes you have to go all in to reap its benefits, other way you're just deluding yourself with another faddish "miracle cure".
All of the above are Meijer's opinions, I'm just the messenger. I know Odersky doesn't share these opinions.
My own opinion: I'm unsure. Though I wonder, these days if you're not a strong FP advocate, and instead look to Scala as an improved Java, why not use Kotlin instead?
This problem with the scala community exists because it is composed of two different philosophical schools of thought. There's the ML camp, which is where we get all of the awesome pragmatic features of the ML family of languages (Martin Odersky has been very clear that Scala is philosophically an ML-family language).
And then we get the Haskell camp, which rightly determined that the Haskell ecosystem was terrible. But instead of adopting an ML mindset of pragmatic functional programming, they decided to bring along their puritan religiosity with them and make the Scala ecosystem terrible too.
The funny thing about Scala's pure functional programming ecosystem is that in its quest to make all programs pure in the sense of not having side effects, they pollute the entire ecosystem with hordes of incompatible dependencies, which need all sorts of bullshit adapters to make them play nice with each other. A far cry from what most people think of with the word "pure".
To quote one of the best essays on the ML philosophy:
> My point is that the ML module system can be deployed by you to impose the sorts of effect segregation imposed on you by default in Haskell. There is nothing special about Haskell that makes this possible, and nothing special about ML that inhibits it. It’s all a mode of use of modules.
> So why don’t we do this by default? Because it’s not such a great idea. Yes, I know it sounds wonderful at first, but then you realize that it’s pretty horrible. Once you’re in the IO monad, you’re stuck there forever, and are reduced to Algol-style imperative programming. You cannot easily convert between functional and monadic style without a radical restructuring of code. And you are deprived of the useful concept of a benign effect.
Add to it the disease that all guest languages suffer from creating idiomatic wrappers instead of just directly calling into the rich ecosystem of the existing platform.
That's true to some extent of the native language as well. Hibernate, for example, is a java(-bean) idiomatic wrapper on top of JDBC. Same goes for Log4J being a wrapper of java.util.logging, etc. Not quite the same thing as what Scala or Clojure have done, but far from just sticking to the basics.
It's one thing to build a wrapper on top of a comprehensive standard library, and another thing altogether to build what is essentially an alternative standard library.
I think calling Hibernate "a wrapper around JDBC" is overreaching. A wrapper brings to mind (at least to me) this idea of a thin layer, mostly a simple translator between two worlds, but in this case there's tons of added complexity and pitfalls on top of JDBC, and there's the whole ORM deal with attending object-relational impedance mismatch, the whole "ORM is the Vietnam of Computer Science", and a completely different can of worms!
Writing higher level abstractions for ease of use in same language as the platform is written on, and creating wrappers just because it looks "ugly" to do direct FFI calls to the host language isn't really the same thing.
In Clojure(Script) it seems to be a net positive because of the curation effect - the community generally has good taste in picking the good parts of the JVM/npm ecosystems and packaging those to for wide use. For example the de facto standard logging library doesn't use log4j but can interop with Java logging stuff and presents the same interface on both ClojureScript and JVM.
(Also many Java libraries have imperative APIs for no good reason, and it's sometimes the wrapper can present a functional interface)
The reason being that they are older than Java 8, and not everyone buys into functional styles.
Even on .NET, with LINQ having been introduced in .NET 3.5, there might come up some PR discussions that I will just give up and rewrite the code to the expectation level of the audience.
Note Robert Harper, the author of Existential Type and one of the big names in ML, has a bizarre crusade against Haskell.
I find it weird that so many of his articles seem to be lashing out against (the internet perception) of Haskell, almost a popularity contest between ML and Haskell, when the adoption of either language is marginal and could use some cooperation between the two, instead of framing it as a zero-sum competition. ML and Haskell are allies, not enemies.
This obsession is a shame, since Robert Harper is obviously an intelligent person. I remember some of his feuds with people from the Haskell community were... embarrassing.
Most feuds are embarrassing. The proglang community could do with fewer of those.
I don't remember Simon Peyton Jones ever speaking ill of ML, for example. There's an example to follow!
the so called "purity" is achieved by using a higher kinded type and the cats IO monad. it's like adding "1+1" with a "mathematical-calculation-monoid-combine-factory-builder-singleton-locator-evaluator-pattern". I remember keeping it simple as an ultimate goal but maybe I am just too for this FP peeing contest? why is it such a frickin' deal to log a message somewhere?
> I remember keeping it simple as an ultimate goal but maybe I am just too for this FP peeing contest? why is it such a frickin' deal to log a message somewhere?
Don't know about others but for me it is about being able to easily verify that my logging is setup correctly.
Once you write your logging to some monad (the library we use does not require it to be IO) testing it becomes trivial with a WriterT monad where it just write your logs into some list/buffer and check that what was logged in various cases was actually what you wanted.
We do the same with tracing checking that spans are created as we wanted and the tags/logs on the spans are setup correctly.
Why would I verify logging which itself is a verification instrument? And, seriously, how hard is it to emit a diagnostic message so I need a test for that to get it right?
If you have some rare (rare as in it happens once every few months or something like that) error and you need logging about it. Also testing it ensures nobody by mistake removes that bit of logging by accident.
> why is it such a frickin' deal to log a message somewhere?
Need I remind you of the Log4J debacle? It's definitely a big deal.
Whether this is the right approach is another matter.
PS: trust me, you don't need to read on category theory to use or understand monoids, which at the level they are used in Scala and Haskell code, they are trivial to understand.
But it doesn't need to. The overall solution, I mean. See another comment in this section mentioning they follow a similar approach but NOT using the IO monad.
Also: you don't need to know category theory to use the IO monad.
Though we have not yet upgraded it to Scala 3 it should work with Scala 3 just fine. (We do compile a Scala 3 release of most of the stuff but the codebase itself is mostly Scala 2 for now)
Can someone please explain the purpose of a logging library (woof, log4j, etc)? There must be something beyond writing text to an external file, possibly with different error levels, that I'm missing. This is a serious question. I truly do not understand what you gain by depending on an external library for this (seemingly) simple operation and would appreciate some insight. Thanks!
I was wondering the same, but a recent explanation made sense to me. The idea is to have the ability to control logging of third-party libraries. You might want to see some log messages but you don't want everything spamming stuff into stdout.
Often logging libraries allow you changing the logging level at runtime, without restarting or recompiling the application, as well as turning logs or off at runtime for different parts of the application. They let you organize logging levels so that when, for example, you turn the level to INFO in one part of the applications, all of the connected code also gets its own log level turned to INFO and you can define which parts of the application should change their log levels in sync. There's also performance considerations, often log libraries claim to implement tricks so that logging is supposed to be faster than naively writing strings to a file.
If it's just writing to a file or the screen you don't need much.
But then you need interpolation of arguments. And other appenders, what about logging to the network for aggregation elsewhere. And logging levels. And a couple more features and corner cases.
And before you know it, you're writing a lot of code which could be packaged into a library.
But you don't need to, like with most library stuff you could write it in-house yourself.
I have done Scala for a long time, I had a chance encounter with Spring boot and reflections like no tomorrow style of programming. It was like a wake up call. I knew why I hated the complex juggernaut of the configs and magic but man do I hate to admit, how productive I was once all that was working. Sure someday it would not work and I would need to debug in my IDE but that was rare.
I have lived the life of somebody who wants to have every effect annotated, still on some level I believe maybe it can be achieved someday with ergonomics. I have done a lot of rust now and when I look back, I look differently at monads and those abstractions now. Maybe I felt smart using them.
There is some value in being able to just look at any library and being able to understand what and how. I have done this a lot in Rust. Scala has some amazing libs and I can't deny that they were immensely powerful, it wasn't easy to make sense of it all though, forget about tweaking and making changes easily.
All in all I have come to realise, there is no perfect paradigm, get work done. Use what makes sense. So stop fretting over dart/go, don't bemoan Kotlin, use Scala when you can and don't worry about making everything a kliesli. And really I would not worry about my logger having a side effect. Effect systems still sound cool though, maybe someday.
Not worrying about programming languages and code golfing has made me a much better and productive developer, I think.
49 comments
[ 12.0 ms ] story [ 241 ms ] thread> Announcing Bark! A logging library written purely in C! Blazingly fast! 0 dependencies! It even prints file and line numbers!
Because it uses an effects system, which are designed to control side effects like the one which caused log4j's vulnerability, it looks like a reaction against log4j, not "more of the same problem".
Whether this is a sound approach or not, I cannot say. But definitely not a case of log4j-like overengineering.
Haskell similarly suffers, I'd like to see IO broken down into composable units of functionality so I can see if e.g. my logging library has some ridiculous network constraint.
You could use other monads though.
> Haskell similarly suffers
Haskell practitioners aim to write as much code as possible outside the IO monad. If everything you write lives inside that monad, what's the benefit?
The nice thing about Haskell is that if you write every function with IO in its signature, it's bound to raise eyebrows in code reviews, whereas in most other mainstream languages everything lives in an IO monad by default!
IO on it’s own could mean disk, network, environment variables, sub processes, etc.
My take away is that they’d like to see IO defined as the union of its parts that can also be decomposed, so you’d have a DiskIO monad or ExecIO
That way you can be more certain that you’re not accidentally going to do a bunch of network io before your sub process kicks off. Right now you can’t have that guarantee (out of the box, at least. idk if there’re any user land libraries to do such a thing)
But there is something on the horizon for Scala:
https://github.com/lampepfl/dotty/blob/release-3.1.0/docs/do...
One can have a proper effect system on the JVM also already today with Flix:
https://en.wikipedia.org/wiki/Flix_(programming_language)#Po...
The old adage holds true: "users only need 20% of this system's features, but the problem is that every user needs a different 20%!"
It's a beautiful and simple language, more so than Scala 2 and I cannot recommend it highly enough for many productive use-cases.
People are willingly recreating the Java situation of 'I don't know the project yet but I know we need Spring!' with these ridiculous libraries from Haskell zealots for no obvious benefit.
The ergonomics of this lib out of the box are literally worse for no reason, and worse than most other existing logging libs, because of it
This happens outside of scala shops, and I've seen it there, too, especially in C++ shops, but scala seems to attract these people.
"Mostly Functional Programming Doesn't Work" [1]
Note it's "mostly functional" that doesn't work, not functional. He believes you have to go all in to reap its benefits, other way you're just deluding yourself with another faddish "miracle cure".
All of the above are Meijer's opinions, I'm just the messenger. I know Odersky doesn't share these opinions.
My own opinion: I'm unsure. Though I wonder, these days if you're not a strong FP advocate, and instead look to Scala as an improved Java, why not use Kotlin instead?
[1] https://queue.acm.org/detail.cfm?id=2611829
So in a sense, he also tried a miracle cure before changing his mind.
And then we get the Haskell camp, which rightly determined that the Haskell ecosystem was terrible. But instead of adopting an ML mindset of pragmatic functional programming, they decided to bring along their puritan religiosity with them and make the Scala ecosystem terrible too.
The funny thing about Scala's pure functional programming ecosystem is that in its quest to make all programs pure in the sense of not having side effects, they pollute the entire ecosystem with hordes of incompatible dependencies, which need all sorts of bullshit adapters to make them play nice with each other. A far cry from what most people think of with the word "pure".
To quote one of the best essays on the ML philosophy:
> My point is that the ML module system can be deployed by you to impose the sorts of effect segregation imposed on you by default in Haskell. There is nothing special about Haskell that makes this possible, and nothing special about ML that inhibits it. It’s all a mode of use of modules.
> So why don’t we do this by default? Because it’s not such a great idea. Yes, I know it sounds wonderful at first, but then you realize that it’s pretty horrible. Once you’re in the IO monad, you’re stuck there forever, and are reduced to Algol-style imperative programming. You cannot easily convert between functional and monadic style without a radical restructuring of code. And you are deprived of the useful concept of a benign effect.
https://existentialtype.wordpress.com/2011/05/01/of-course-m...
It's one thing to build a wrapper on top of a comprehensive standard library, and another thing altogether to build what is essentially an alternative standard library.
(Also many Java libraries have imperative APIs for no good reason, and it's sometimes the wrapper can present a functional interface)
Even on .NET, with LINQ having been introduced in .NET 3.5, there might come up some PR discussions that I will just give up and rewrite the code to the expectation level of the audience.
I find it weird that so many of his articles seem to be lashing out against (the internet perception) of Haskell, almost a popularity contest between ML and Haskell, when the adoption of either language is marginal and could use some cooperation between the two, instead of framing it as a zero-sum competition. ML and Haskell are allies, not enemies.
This obsession is a shame, since Robert Harper is obviously an intelligent person. I remember some of his feuds with people from the Haskell community were... embarrassing.
Most feuds are embarrassing. The proglang community could do with fewer of those.
I don't remember Simon Peyton Jones ever speaking ill of ML, for example. There's an example to follow!
I'm not familiar with Woof but it looks like an attempt at writing a pure logging lib. What is wrong with that?
Don't know about others but for me it is about being able to easily verify that my logging is setup correctly.
Once you write your logging to some monad (the library we use does not require it to be IO) testing it becomes trivial with a WriterT monad where it just write your logs into some list/buffer and check that what was logged in various cases was actually what you wanted.
We do the same with tracing checking that spans are created as we wanted and the tags/logs on the spans are setup correctly.
Need I remind you of the Log4J debacle? It's definitely a big deal.
Whether this is the right approach is another matter.
PS: trust me, you don't need to read on category theory to use or understand monoids, which at the level they are used in Scala and Haskell code, they are trivial to understand.
Also: you don't need to know category theory to use the IO monad.
https://dotty.epfl.ch/docs/reference/other-new-features/inde...
"Don't be snarky."
https://news.ycombinator.com/newsguidelines.html
It also has some "fancy" features like changing log level if exception happens
https://github.com/valskalla/odin#extras-conditional-logging
Though we have not yet upgraded it to Scala 3 it should work with Scala 3 just fine. (We do compile a Scala 3 release of most of the stuff but the codebase itself is mostly Scala 2 for now)
But then you need interpolation of arguments. And other appenders, what about logging to the network for aggregation elsewhere. And logging levels. And a couple more features and corner cases.
And before you know it, you're writing a lot of code which could be packaged into a library.
But you don't need to, like with most library stuff you could write it in-house yourself.
I have lived the life of somebody who wants to have every effect annotated, still on some level I believe maybe it can be achieved someday with ergonomics. I have done a lot of rust now and when I look back, I look differently at monads and those abstractions now. Maybe I felt smart using them.
There is some value in being able to just look at any library and being able to understand what and how. I have done this a lot in Rust. Scala has some amazing libs and I can't deny that they were immensely powerful, it wasn't easy to make sense of it all though, forget about tweaking and making changes easily.
All in all I have come to realise, there is no perfect paradigm, get work done. Use what makes sense. So stop fretting over dart/go, don't bemoan Kotlin, use Scala when you can and don't worry about making everything a kliesli. And really I would not worry about my logger having a side effect. Effect systems still sound cool though, maybe someday.
Not worrying about programming languages and code golfing has made me a much better and productive developer, I think.
https://www.youtube.com/watch?v=yL1z1ZHD0K4
(wuphf.com)