6 ms·
I see a lot of “coding” talk in the blog and comments from the author here, but few mentions as to what kind of software they’re building or what use cases they
by opportune 3y ago
I see a lot of “coding” talk in the blog and comments from the author here, but few mentions as to what kind of software they’re building or what use cases they’re targeting.
My hot take is that the reason functional programming never took off is that, while it certainly is fine for writing programs, most software these days is not “program running locally on my pc/server from the command line until it completes” and is instead “program that starts, reacts to input from user, then gets closed by the user” or “program that starts, then responds to network or other automated I/O (to serve web pages, to monitor something, to emit logs, etc) then stops when the other software tells it to”. This is a lot harder to do in a purely functional style, or at least it is in most opinionated functional programming implementations I’ve used, because you’re no longer “just” evaluating some expression but instead initializing state, reacting to I/O, then updating state and/or performing further I/O potentially while using parallelization to perform monitoring/listen for other things/perform further I/O and state updates.
Of course it’s not impossible to do these things with Lisp but from my couple of semesters of exposure of FP in undergrad and use of FP features in C++ and Scala professionally to solve these kinds of problems… it seems quite hard to get FP to work for these applications, and that lack of suitability is what discourages me from diving more fully into FP
- lukego 3y agoI'm using Lisp for simulation. It's really wonderful being able to poke and prod long-running computations while they run. I missed this too much when I tried using Julia.
- vippy 3y agoIt took me a while to grok monads, and the IO monad, and longer still to figure out how to compose them in safe ways, and manipulate execution order, etc. But: now I can write typesafe applications, and I produce fewer bugs when I work in non-FP languages (I get paid to write Java.) Lisp is a starting point. Haskell is where it's at. I recommend learning the style, even if you never produce production code in it.
- xedrac 3y agoYes, Haskell is magnificent for learning FP. I used to think Haskell was terrible for IO, but my tune has changed dramatically since I started working with it full time.
- opportune 3y agoLet’s say I want to do something simple but slightly beyond the scope of a traditional toy demonstration: * Read some environment variables and a local file * Start a monitoring thread that consumes from a channel or something similar, then every X s or X events writes to a local temp file and then sends a request batching some metrics to an external system * Configure and start an http server * Said server has a handler that 0. Starts a timer 1. loads, then increments an atomic “num requests served until now” variable 2. uses synchronization to lock on a list or ring buffer containing the last 5 requests’ user-agent headers 2.5 copies the current last 5 values, replaces oldest one with the one from the handles request, unlocks 3. generates a json response containing like “num_so_far: x, last5agent: [..], “some_env_var”:..” 3.5 stops the timer 4. write request user agent and time interval to monitoring thread’s channel 5. write response and end handling * server’s gotta be able to do concurrency > 1 with parallelism * On sigterm set the server to a state that rejects new requests, waits for existing requests to complete, then flushes the monitoring channel I’d consider this a trial run of some of the most basic patterns commonly used by networked software: init io, immutable shared state, atomic mutable shared state, synchronization locked shared state, http ingress and egress, serialization, concurrency, parallelizarion, background threads, os signals, nontrivial cleanup. In Go, Java, or C++ I could write this with my eyes closed. How easy is it in Haskell or Lisp? If you know of any demos or repos that do something like this - not a pure toy or barebones demo, but not a huge task all in all- in either I’d be interested in seeing what it looks like.
- remexre 3y agoI'll cover the Haskell side because I'm more familiar with its library ecosystem: > Read some environment variables and a local file import System.Environment (getEnv) import System.FilePath ((</>)) main = do dir <- getEnv "WHATEVER_DIR" data <- readFile (dir </> "foo.txt") putStrLn ("Had " ++ show (length (lines data)) ++ " lines") > Start a monitoring thread [...] import Control.Concurrent import Control.Concurrent.Chan import Network.HTTP -- also more stuff... monitoringThread :: Chan String -> IO () monitoringThread chan = do file <- openFile "log.txt" AppendMode forever $ do -- no $ needed if you pass -XBlockArguments [0, 1] batch <- replicateM 5 (readChan chan) let chunk = unlines batch hPutStr file chunk simpleHTTP (postRequestWithBody "" "text/plain" chunk) main :: IO () main = do logChan <- newChan void (forkIO (monitoringThread logChan)) -- ... forever $ do threadDelay (1000 * 1000) -- 1M usec writeChan logChan "Hello, world!" > Configure and start an http server import Network.Wai import Network.Wai.Handler.Warp main = run 8000 $ \req respond -> respond (responseLBS status200 [] "Hello, world!") > Said server has [...] Yeah, this is long. If you're just getting the current time with the timer, that's here[2]; synchronize across threads with MVars[3] or STM[4]; JSON is in aeson[5], which should feel broadly familiar if you know Rust's serde. > server’s gotta be able to do concurrency > 1 with parallelism Yep, GHC Haskell has _excellent_ concurrency support on top of a parallel runtime. > On sigterm set the server to a state that rejects new requests, waits for existing requests to complete, then flushes the monitoring channel I haven't personally tried this, but this[6] function sounds like... exactly this, actually, so I think its example should suffice? On two separate notes: - Common Lisp and Python 3 are a _lot_ closer than Common Lisp and Haskell, or even Python 3 and JavaScript; the Python 3 object model is very close to Common Lisp's, and Common Lisp is not particularly pure (setf isn't unidiomatic by a longshot), and supports a very non-functional style of programming (it has gotos!). - "Haskell is worse at IO than other high-level languages" isn't particularly true. What _is_ true is that Haskell has the same "function coloring problem" as JavaScript (Haskell has the IO monad, JavaScript has the Promise monad); Haskell also has a "uses funny academic words" problem (well, debatably a problem...) which I think confuses the issue. [0]: https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/block_arguments.html https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/bloc... [1]: Haskell has a spec, one "main" implementation (GHC), little spec-committee activity, and a respect for that implementation not superseding the spec; many improvements become language extensions (-X flags or {-# LANGUAGE #-} pragmas), so when you invoke GHC you're getting a by-the-spec implementation by default. [2]: https://hackage.haskell.org/package/time-1.12.2/docs/Data-Time-Clock.html#v:getCurrentTime https://hackage.haskell.org/package/time-1.12.2/docs/Data-Ti... [3]: https://hackage.haskell.org/package/base-4.18.0.0/docs/Control-Concurrent-MVar.html https://hackage.haskell.org/package/base-4.18.0.0/docs/Contr... [4]: https://hackage.haskell.org/package/stm-2.5.1.0/docs/Control-Concurrent-STM-TMVar.html https://hackage.haskell.org/package/stm-2.5.1.0/docs/Control... [5]: https://hackage.haskell.org/package/aeson-2.2.0.0/docs/Data-Aeson.html https://hackage.haskell.org/package/aeson-2.2.0.0/docs/Data-... [6]: https://hackage.haskell.org/package/warp-3.3.28/docs/Network-Wai-Handler-Warp.html#v:setInstallShutdownHandler https://hackage.haskell.org/package/warp-3.3.28/docs/Network...
- rileyphone 3y agoNote that Common Lisp contains CLOS, which is one of the most advanced object-oriented systems even now. Most Lisps are not functional like Haskell is.
- opportune 3y agoI read this Wikipedia article and some examples: https://en.m.wikipedia.org/wiki/Common_Lisp_Object_System https://en.m.wikipedia.org/wiki/Common_Lisp_Object_System Yes, the article calls it powerful, but aside from the ability to update classes and their functions at runtime - which, maybe I’m missing the utility of so I won’t say it’s useless although in my experience a SharedInstanceSingleton or LocalImmutableConfig or BatchedMonitoringEvent wouldn’t need it - it’s “just” dynamically resolving the method implementation to use based on argument types dynamically and letting you provide an order of precedence for diamond inheritance. I think it does solve one problem I have - with some solid tooling and a guarantee that class updates at runtime don’t disrupt ongoing calls, it might let me patch running binaries without doing a full update. Though, the cost is that I seem to incur a few extra lookups or even sorts on each dynamically dispatched method, to resolve the implementation? Besides that it doesn’t seem to really solve most problems I have regarding I/O or state - having the option to update some of these at runtime is interesting but it seems like something I’d only want to selectively enable
- varjag 3y agoin my experience a SharedInstanceSingleton or LocalImmutableConfig or BatchedMonitoringEvent wouldn’t need it When you can't have something, it often becomes felt that you don't need something.
- schemescape 3y ago> I see a lot of “coding” talk in the blog and comments from the author here, but few mentions as to what kind of software they’re building or what use cases they’re targeting. Good point! This is all currently just a hobby. For Common Lisp specifically, the only things I've produced are a (mediocre) Battlesnake client and a (now defunct, as of yesterday) multiplayer word scramble game. Neither of these really derives much benefit from being created in Lisp, but I learned a lot along the way (which was really the point). Unrelated to Common Lisp, I've found myself often needing to write code that generates code. This is an area where I suspect Lisp will shine, although I haven't had a chance to give it a try yet. Two examples from recent projects (which I tackled before ever thinking about using Common Lisp) are: * Generating code to validate a particular JSON Schema (used in a static site generator) * Generating JSX from Markdown (used for story content in a programming game) To say nothing of the innumerable C macros I've written in my lifetime :)
- opportune 3y agoThanks for the answer! Your word scramble game in particular seems like something that approximates my “maybe not a good fit for FP” bucket. Do you plan on sharing it on GitHub or describing the challenges you ran into? Completely agree code generation is where I expect Lisp to perform the best. Though, I looked it up and apparently Markdown is not context-free so.. curious as to the challenges that introduces as I figure FP could zap through parsing a CFG but really struggle with state for something not context free
- schemescape 3y agoNote that Common Lisp doesn’t require functional programming. Mutation, side effects, etc. are fine. I just write imperative code for the most part. My code was quick and dirty, so I don’t think anyone will learn anything from it, but it’s here: https://github.com/jaredkrinke/thirteen-letters https://github.com/jaredkrinke/thirteen-letters
- victorbjorklund 3y agoWhatsapp and discord runs on functional elixir/erlang. I heard they are pretty big and not hobby projects.
- kubb 3y agoFunctional programming took off big time. Just look at the JavaScript ecosystem.
- lawn 3y agoElixir uses functional programming and its excellent for web development and whenever you want a fault tolerant system. You also don't need to throw out all the good features of the other styles, as parts of functional programming are becoming more and more common in "regular" languages too. Rust uses functional patterns in many cases for instance. And you can also write Lisp in an OO or imperative style if you want, it's no Haskell.
- tmtvl 3y agoCommon Lisp isn't a "purely functional" language, it supports every paradigm. It allows silly things like... (let ((pair (cons 1 nil))) (setf (cdr pair) pair) (list (first pair) (second pair) (third pair))) ;; => (1 1 1)