Let's Take a Trivial Problem and Make it Hard
prog21.dadgum.com
prog21.dadgum.com
let c' = c // [(i, c!i + 1)]
To quote the documentation: "When the // operator is applied to a diff array, its contents are physically updated in place. The old array silently changes its representation without changing the visible behavior: it stores a link to the new current array along with the difference to be applied to get the old contents."
For more information: http://www.haskell.org/ghc/dist/current/docs/libraries/array...
I don't know if something similar exists for Erlang, but if not, I assume it wouldn't be terribly hard to implement.
You can use unsafePerformIO in your own code if you really need it--it's basically a giant loophole in the type system, so if you don't know what you're doing, you can screw yourself, but if you really need that power, it's there.
More here: http://www.haskell.org/haskellwiki/IO_inside#Dark_side_of_IO...
This is why using a multiparadigm language (like OCaml or the various Lisps) or an FFI to work in a few complementary languages is often a practical approach. Working solely in a pure-(one thing) language often involves over-committing to a specific trade-off.
Edit: Or, yes, the imperative sub-language embedded in the state monad.
However, functional solutions to this specific problem are not cumbersome at all. See for example this snippet of literate Haskell. It assumes that you have already put your binary data in a list of chars and return a Map (similary to a Python dict) of frequencies:
> import qualified Data.Map as M
> freqMap :: [Char] -> M.Map Char Int
> freqMap list = foldr op M.empty list
> where op :: Char -> M.Map Char Int -> M.Map Char Int
> op c freq_map = M.insertWith (+) c 1 freq_mapI'm not great at Haskell (GHC > 6.6 won't install on my main computer... http://hackage.haskell.org/trac/ghc/ticket/1346 ), and while I understand the type system etc. from using OCaml, in that, I would just do this imperatively and give it a functional interface.
I'm not just being difficult, I'm curious how you'd go about it. (Edit: Cool, thanks.)
This is the same laziness that lets Haskell operate on infinite lists like [1..]
There's still a lot for me to learn about how lazyness impacts memory requirements. (Some times Haskell was lazier than I was expecting and kept thunks in memory that were bigger than their eventual result.)
Although I still have to learn a lot, lazyness by default brings more benefits than it costs IHMO.
Talk about, "Let's Take a Trivial Problem and Make it Hard".
For the general problem of doing things purely where you would otherwise use destructive updates, you need a type system that lets the compiler safely and correctly figure things out about your program. Sheesh.
At the same time, many programs in the real world get by without sophisticated type systems. They're a great tool, but they're also probably vastly more useful in some problem domains than others. Same with OOP, constraints and backtracking, relational databases, etc.
(That said, I would love to use an ML-like language as small & clean as Lua. Inferred, garbage-collected, recursive tagged-union types rule.)
Will there ever be "one paradigm to rule them all"?
My guess is no. This type of simple, trivial problem screams for a structural, imperative approach. Other problems call clearly for mutable objects, others for declarative DSLs, and yet others for a pure functional approach.
Functional purists will tell you that mutable state is ALWAYS bad. OOP purists will tell you that straight-structured programming is ALWAYS bad, and structural purists will tell you that hand-optimized but difficult to understand assembly code is ALWAYS bad.
They're all wrong, of course. Pick the right tool for the right job. Now, it's true that not all programmers are skilled enough to do that, but I consider that to be a project management/source control problem, not a language problem.
Of course, this has a first (minor) problem: You need to know multiple languages. However, I consider the "problem" of many people not knowing multiple languages a chicken-and-egg-problem. If you just know a single programing language and never learn a second one, you will not learn how to learn programming languages. If it becomes very natural to know many languages, learning a new language becomes very, very simple (I for myself found it to be a very good way to get a good grasp on the runtime model of the language.Once you know the runtime model of a language, actually programming it is easy).
However, the worse problem is: Grasping and actually designing things in the UNIX-way is hard. In fact, hard is not the right term. It is _different_ from the way such design was taught to me. I for myself recently massively restructured a (simple) code generator into many smaller programs, communicating via pipes. At first, this felt very akward, because.. it was different? However, now that I am implementing and using this, the new version does have massive benefits. (In fact, webservices appear to be the unix-philosophy in different clothes). So I guess it is just a problem with peoples minds.
The Unix style seems to fit together relatively poorly with complex type systems, though. Some programs (compilers, in particular) need very little interaction with the outside world, and tend to require complex internal data structures. Again, no one paradigm fits everything.
Really? The functional programming people I know tend to say that unsegregated mutable state is bad. That's why Haskell has state monads: so you can build a wall around the parts of the code which have mutable state, and present a purely functional facade to the rest of the code.
> erlang:put(1,1).
undefined
> erlang:get(1).
1
> erlang:put(1,erlang:get(1)+1).
1
> erlang:get(1).
2
> erlang:put(2,3).
undefined
> erlang:get().
[{1,2},{2,3}]
It's limited to a single process and thus doesn't break the 'no-shared-memory' model. It also lets you access and modify data without copying it, so the closest implementation of such a function would need to be defined that way and would only return the final list.Of course, this breaks the idea of 'purely functional' which is what the author found problematic.
Unless I am mistaken about his suggestion, O(n log(n)) isn't quite "not in the same ballpark" as O(n), and if you replace a suggested tree with a data structure with O(1) lookup then we're precisely in the same ballpark.
And what's up with this passive-aggressive attitude?