I'm David Feuer. I maintain containers, co-maintain unordered-containers and pqueue, and contribute to various other projects. Data structures are fun. I'm a good person to talk to about laziness subtleties, and about whether particular applications of unsafe IO are safe in context.
The discussion about incentives for stability was interesting. It reminded me of the maintainership standards proposal. I think it would be very useful to have Hackage show information like how quickly a package fixes version bounds when new versions of their dependencies are released.
This was a fun episode. I was introduced to breadth first labeling and attribute grammars by Doaitse Swierstra at the Applied Functional Programming summer school in Utrecht. He was an inspiring figure.
The biggest disadvantage of circular programs is that it is very easy to get into infinite loops when you make mistakes. I wish there was an easy way to guarantee statically that circular programs are terminating (perhaps using types).
@jaror@bss03 Maybe I was wrong, but I think you can do Scott encoding of the GADT underneath the standard codensity representation of existentials via CPS. Still need higher-rank types, not "just" parametricity.
I should write up some code to check myself against GHC.
You can swap out x for y to see the behavior is the same.
You can drop the GADT pragma, GADT definition, f, existential, g, and x (but keep all the Scott versions, includeing y) to reveal code that works “simply” with RankNTypes.
Higher-rank types and parametricity is quite powerful.
BTW, this isn’t new / doesn’t require the bleeding edge compiler. I’m on “The Glorious Glasgow Haskell Compilation System, version 9.0.2” from the Debian repositories.
The Lemmy->Kbin conversion has inserted a lot of <span> elements into your code making it unreadable. For people reading this from the Kbin side, here's the code:
{-# language GADTs #-}
{-# language RankNTypes #-}
import Data.Functor.Const
-- The GADT
data AGADT a where
I :: [Integer] -> AGADT Integer
S :: String -> AGADT String
type Scott_GADT a = forall fr. ([Integer] -> fr Integer) -> (String -> fr String) -> fr a
f :: AGADT a -> String
f (I x) = show x
f (S x) = x
f' :: Scott_GADT a -> String
f' x = getConst $ x (Const . show) Const
-- The Existential
data AnyGADT = forall a. MkAnyGADT (AGADT a)
type Scott_Any =
forall r.
(forall a. (forall fr. ([Integer] -> fr Integer) -> (String -> fr String) -> fr a) -> r) ->
r
g :: String -> AnyGADT
g "foo" = MkAnyGADT (I [42])
g "bar" = MkAnyGADT (I [69])
g x = MkAnyGADT (S x)
g' :: String -> Scott_Any
g' "foo" x = x (\i _s -> i [42])
g' "bar" x = x (\i _s -> i [69])
g' s x = x (\_i s' -> s' s)
main = interact (unlines . fmap x . lines)
where
x s = case g s of { MkAnyGADT x -> f x }
y s = g' s f'
I think the spans are all syntax highlighting/coloring. Your comment seems to have a dangling ```/span at the end to me, but that might just be the KBin->Lemmy translation.
EDIT: Also, Lemmy seems to be munging this post between the preview and the submit, due to me wanting to include some text that appears to be a dangling xml/html end tag (angle brackets removed in edit for “readability”) inside backticks.
Ah, that's interesting. Although I can imagine not many people would want to write code in that style. And I also wonder how many languages support higher rank polymorphism in the first place.
Yeah, I generally prefer pattern matching and constructor calls, but often languages don’t have GADTs or existentials. Even in GHC, existentials are still a bit “wonky”, though still generally nicer to use than CPS/Codensity.
I'm always surprised by how much controversy there is online about laziness and space leaks, but almost all these industry interviews from Serokell includes lines such as:
Mats: Most of the things that people warn online such as laziness, memory leaks, hiring haven’t been problems so far. The biggest problem we have faced relates to upgrading the dependencies.
@jaror second one was even more awesome than the first one! This view is something that makes it really possible to argue for laziness by default! Thank you @lexi_lambda!
Semantically, eager languages are “strict”, while lazy languages are “non-strict”.
Strict means that f _|_ = _|_, that is to say, that passing an error value of any sort to a function must produce an error value. A lazy language is one where that isn’t a universal requirement.
In a lazy language, you can have head (genList 1000) = 1000. In an eager language, it must be the case that head (genList 1000) = error “too long”, otherwise you’ve broken the semantics of the language. That requires actually evaluating the whole list.
That’s part of why laziness in strict languages has to be explicitly opt-in
@jaror yes very nasty, I reported an issue and three bugs were found investigating it. Romes and Ben did great work doing so. It were multiple issues with pointer tagging.
I'm Glyn Normington. I dipped into Haskell briefly in about 2011, but recently got a little deeper into it when I taught a second year functional programming module at Winchester University, UK. I'm a retired programmer with 39 years experience and try to pass on some general tips in the module. I came here after recently deleting my Reddit account. (I've been on the Fediverse for a while as fosstodon.org/@underlap.)
I'm Kleidukos, interested in production Haskell, with a focus on developer experience, web services, accessibility and building community spirit. Glad to join the party here!
I'm "TomMD". To say I 'maintain' things might be an overstatement, but I've released a number of Haskell packages and minor fixes. Much of that was on my own but a significant portion were from my Galois days. I've had a few publications and am hoping to get to ICFP Seattle this year. Also years ago I made (cofounded) a company (Muse Dev) with Haskell as central part of the stack which was enjoyable.
Currently? Life is very busy so I'm just enjoying my Haskell time writing this and that rather than building much for release.
I'm Edwin. I've been enamored by Haskell for several years, but never had the opportunity to work on it professionally, or for any medium-to-large sized projects.
I did write a statistics calculator for DnD 5e for my DM that used Haskell for the backend though, and that was fun. I attempted to use Haskell for the GUI, but found the experience lacking, since my DM had a requirement that it work on Windows and be a desktop app. Long story short, I spent hours trying to get gi-gtk to work on Windows after spending other hours trying various other solutions (to include threepenny-gui) before eventually giving up and writing the GUI in Python + Qt 6.
I now have a passion project named "War Womb", which aims to be a 2D app that lets you play Warcaster: Neo-Mechanika digitally. I have a prototype written as a web-app using Python + FastAPI in the backend and Typescript + React on the frontened. I've been recently tinkering with SDL to see if I can treat the app more like a game, since there are a lot of interactive components, and thus hopefully use Haskell for this project instead, since I have way more fun programming in Haskell than anything else I've use.
Both projects sound cool! I've also experienced issues with GUI programming in Haskell. It seems once upon a time wx worked well, but now it is no longer maintained.
Ok, I took a look. The last comment was from more than a year ago, unfortunately. I think I got gtk to finally work. I just wish the gtk4 API changes hadn't forced a need to use implicit params :-(
data Stream a = forall s. Stream !(s -> Step s a) !s
data Step s a = Yield a !s | Skip !s | Done
data Tup a b = Tup !a !b
cartesianProduct :: Stream a -> Stream b -> Stream (a, b)
cartesianProduct (Stream step1 s01) (Stream step2 s02) = Stream step' s' where
s' = Tup s01 s02
step' (Tup s1 s2) =
case step1 s1 of
Yield x s1' ->
case step2 s2 of
Yield y s2' -> Yield (x, y) (Tup s1 s2')
Skip s2' -> Skip (Tup s1 s2')
Done -> Skip (Tup s1' s02)
Skip s1' -> Skip (Tup s1' s2)
Done -> Done
eft :: Int -> Int -> Stream Int
eft x y = Stream step x where
step s
| s > y = Done
| otherwise = Yield s (s + 1)
fooS :: Stream (Int, Int)
fooS = cartesianProduct (eft 0 10) (eft 0 10)
toList :: Stream a -> [a]
toList (Stream step s0) = go s0 where
go !s =
case step s of
Yield x s' -> x : go s'
Skip s' -> go s'
Done -> []
foo :: [(Int,Int)]
foo = toList fooS
Haskell
Hot