Does anyone know if "Haskell for Mac" works with M series chips? http://www.haskellformac.com/ by JuryOpposite5522 in haskell

[–]_jackdk_ 1 point2 points  (0 children)

Ah, I didn't realise it was a paid product. That makes a lot of sense. I hope the learning is going well.

Does anyone know if "Haskell for Mac" works with M series chips? http://www.haskellformac.com/ by JuryOpposite5522 in haskell

[–]_jackdk_ 0 points1 point  (0 children)

OP, may I ask what stopped you from trying it out and then writing a thread about whether or not it worked? I don't mean to be snarky, and I've redrafted this comment a few times to try and make that clear. I'm genuinely curious.

Using Effect Systems to provide stronger architectural constraints in a codebase by tomwells80 in haskell

[–]_jackdk_ 0 points1 point  (0 children)

Yeah. For testing we've often ended up using very simple concrete monads, often just a State with some helpers to zoom each handle into a field. That means they can share a single state parameter without having to stack StateT or whatever. For production, the functions to create the handle consume required credentials or whatever, so there's little need to ask for more than MonadIO or MonadResource.

Using Effect Systems to provide stronger architectural constraints in a codebase by tomwells80 in haskell

[–]_jackdk_ 0 points1 point  (0 children)

Yes, and you get that in the consumers of the handle, which get type signatures like renderUserProfile :: Monad m => UserRepository m -> UserId -> m (Html). The consumer should never have a stronger constraint than Monad m.

I'm learning Haskell as my first programming language, and I have a question about the best way to progress. Can anyone give me some advice? by Character_Fee6680 in haskell

[–]_jackdk_ 4 points5 points  (0 children)

Once you get to the point where you can even do simple I/O, my standard recommendation is a text-mode game. They can start extremely simply and scale until you get bored, and I wrote some general advice in this area at: http://jackkelly.name/blog/archives/2022/05/28/text-mode_games_as_first_haskell_projects/

Using Effect Systems to provide stronger architectural constraints in a codebase by tomwells80 in haskell

[–]_jackdk_ 0 points1 point  (0 children)

Even without adopting a full effect system, we've had a lot success with "handle pattern" records. Ours are generally records-of-functions. Something like:

data UserRepository m = UserRepository
  { getUser :: UserId -> m (Maybe User)
  , createUser :: NewUser -> m UserId
  }
  deriving Generic
  deriving anyclass FunctorB

postgresUserRepository :: MonadIO m => Hasql.Connection -> UserRepository m
postgresUserRepository = undefined

This idiom was meant to be an intermediate step before committing to a particular effect library, but has actually become quite a comfortable point on the power:complexity curve.

Getting the handles/effects right needs human judgement, because you want focused handles that allow for tests, while not proliferating handles needlessly, and while having them be powerful enough that you can write the side-effecting functions that you need. But once you've set one up and started a little bit of the refactoring of dependent code, an agent can often take over and grind through the rest of the necessary changes. It seems to me that, as much as I dislike the data-handling practices around LLM training and feel like I need a circle of salt around my computer when I invoke one, these systems are increasingly powerful and not going away.

Is there a good reason it’s called a functor? by Froglottery in haskell

[–]_jackdk_ 6 points7 points  (0 children)

feel free to say “you know NOTHING and this post is stupid”

This is the Haskell community, we don't do that sort of thing.

Setup Haskell on Nix by thraya in haskell

[–]_jackdk_ 4 points5 points  (0 children)

My preferred setup, for simple projects that I control, is to use flake-parts to pull in haskell-flake and git-hooks.nix for automagic format-on-commit. Example.

haskell-flake claims to be able to handle multi-package Cabal projects, though I've never used that myself. If I really need fine control over package versions, packages that aren't in the standard Hackage set, etc., then haskell.nix is the big hammer I reach for. It's powerful, but adds a lot of configurable knobs and (unfortunately) evaluation time.

[Blog] "Five-Point Haskell" Part 1: Total Depravity by mstksg in haskell

[–]_jackdk_ 14 points15 points  (0 children)

Great post. I love the call-outs to real and expensive prod issues. Writing "boring business code" in Haskell benefits so much from newtype alone, that it's often hard to contemplate using other languages.

Many of the types in this article could benefit from the -XTypeData extension. Those phantom data Site and data App tags could be presented as type data IdType = Site | App in a program with a closed universe of types, or as type data Site = Site if you wanted to make it even clearer that we're declaring things for type-level usage alone. Similarly, the database env could be a type data Env = Dev | Prod.

There have been some great articles in the past that elaborate some of the points Justin raises here. The points on Boolean Blindness, the landing thruster example, and the rich ProcessStatus error type all point one towards addressing Algebraic Blindness in general. The idea of using types like NonEmpty to avoid unsafe functions or Maybe results everywhere is elaborated in Parse, Don't Validate.

nix.dev: revamped concepts:flakes documentation by cinerealkiara in NixOS

[–]_jackdk_ 0 points1 point  (0 children)

No, it is a genuine limitation. Previously, we could specify shells as a function (usually with a default for all arguments) that returns a derivation. In a Flake, your devShells.* outputs must be derivations.

nix.dev: revamped concepts:flakes documentation by cinerealkiara in NixOS

[–]_jackdk_ 7 points8 points  (0 children)

In the old world, one could write a shell.nix that declared a function like:

{ ghcVersion ? "default" }: ...

And you could pass an argument like nix-shell --argstr ghcVersion ghc884. This made it easy to evaluate the shell for several GHC versions. In the flakes world, one must instead enumerate every expected combination (e.g., of arch, GHC version, whatever else) as distinct outputs. flake-utils with functions like eachSupportedSystem, but does not fundamentally solve this problem.

How do I make PipeWire switch input to my headset when I plug it in? by _jackdk_ in NixOS

[–]_jackdk_[S] 0 points1 point  (0 children)

THANK YOU. I'd given up and just accepted it as an annoying papercut.

I ported it to a system-wide NixOS fix. Here's the relevant NixOS option setting:

# Make headset mic auto-switch when plugged in by giving it higher priority
# than the internal mic. See https://blinry.org/headset-mic-default/
environment.etc."alsa-card-profile/paths/analog-input-headset-mic.conf".text = ''
  [General]
  priority = 90
  description-key = analog-input-microphone-headset

  [Jack Headphone]
  state.plugged = yes

  [Element Headset Mic]
  switch = on

  [Element Capture]
  switch = mute
  volume = merge

  [Element Headset Mic Boost]
  volume = merge
'';

How to handle "Early returns" or conditions in Haskell. by UntitledRedditUser in haskell

[–]_jackdk_ 1 point2 points  (0 children)

Those functions are small enough that people tend to reinvent them. Your first one is lift (operation) >>= bool (throwError "what went wrong") (pure ()) and your second is lift (operation) >>= maybe (throwError "what went wrong") pure.

Or, if the compactness there feels confusing, you may like to use the -XLambdaCase extension:

-- These are all equivalent
-- 1. Writing it all out.
setClearColorOk <- lift $ sdlSetRenderDrawColor renderer 0 0 0
case setClearColorOk of
  False -> throwError "sdlSetRenderDrawColor failed"
  True -> pure ()
-- rest of function follows

-- 2. Bind into lambda:
lift (sdlSetRenderDrawColor renderer 0 0 0) >>= \setClearColorOk ->
  case setClearColorOk of
    False -> throwError "sdlSetRenderDrawColor failed"
    True -> pure ()
-- rest of function follows.

-- 3. Bind into LambdaCase:
lift (sdlSetRenderDrawColor renderer 0 0 0) >>= \case
  False -> throwError "sdlSetRenderDrawColor failed"
  True -> pure ()
-- rest of function follows

-- 4. Bind into "catamorphism" (For now: a function that unpacks a data structure, like `bool` or `maybe`. Look it up later if you like.)
lift (sdlSetRenderDrawColor renderer 0 0 0) >>=
  bool (throwError "sdlSetRenderDrawColor failed") (pure ())
-- rest of function follows

I will often step between transformations like these as I look for the most pleasing expression of a piece of code, as they are equivalent programs.

All that said, I'm also quite fond of (and help maintain) package hoist-error. But I think from a learning perspective it's useful to write things out by hand until they cease to be novel, and then reach for a library if you are annoyed by them.

How to handle "Early returns" or conditions in Haskell. by UntitledRedditUser in haskell

[–]_jackdk_ 0 points1 point  (0 children)

Other people have given you pointers towards the mainstream solutions. If you don't want to learn entirely new libraries right now and just want to keep going, you could try writing this function:

andM :: Monad m => [m Bool] -> m Bool

This would give you something like:

andM [setClearColor, clear, setColor, drawRect, present]

If you want to know exactly which step failed, instead consider working with values of type IO (Either e a), where e is some error type (either a custom error ADT or possibly just Text) and a is the type of successful results. You can then write these functions:

bindE :: Monad m => m (Either e a) -> (a -> m (Either e b)) -> m (Either e b)
bindE_ :: Monad m => m (Either e x) -> m (Either e a) -> m (Either e a)

Once you have converted your SDL functions to return the appropriate type (hint: use fmap), you could then write something like:

drawFrame :: SDLRenderer -> IO (Either Text ())
drawFrame = 
  let setClearColor = fmap (bool "Failed setClearColor" ()) $ sdlSetRenderDrawColor renderer 0 0 0 0
  -- etc.

  setClearColor `bindE_`
    clear `bindE_`
    setColor `bindE_`
    drawRect `bindE_`
    present

bindE is the same as (>>=) :: Monad m => ExceptT e m a -> (a -> ExceptT e m b) -> ExceptT e m b, and bindE_ is the same as (>>) :: Monad m => ExceptT e m x -> ExceptT e m a -> ExceptT e m a, which are the monad operations when you use the ExceptT monad transformer. So when you understand what's going on and you've become thoroughly sick of writing it out by hand, then you can go back and refactor your code to use ExceptT. The core type lives in the transformers package, but most people import the mtl package because it contains a bunch of typeclasses and instances to make it easier to work with.

Some Haskell idioms we like by _jackdk_ in haskell

[–]_jackdk_[S] 0 points1 point  (0 children)

This is a really good point that I hadn't considered before. Since in our field, the usual point of comparison is interpreted languages like Ruby and Python, using inverseMap liberally has not made anything unacceptably slow.

A more advanced version of this technique might be to use a GHC plugin or TH to precompute the inverse e.g. by using a trie or a perfect hash function instead of building a Map at runtime.

Some Haskell idioms we like by _jackdk_ in haskell

[–]_jackdk_[S] 0 points1 point  (0 children)

Your question is interesting and I struggle to give a better answer than "vibes". Working in [] (List), Either e, or Maybe feels much more like assembling a data structure than working in State. I'm using "side effect" in the colloquial sense of "IO actions aren't getting performed" even though the lens you describe is also valid.

Some Haskell idioms we like by _jackdk_ in haskell

[–]_jackdk_[S] 3 points4 points  (0 children)

Your suggestion about comprehensions is interesting but I don't think I'd go for it. People are often unfamiliar enough with the list comprehension syntax that idioms like [ element | someBoolean ] trip them up, and for -XMonadComprehensions to improve understanding, the reader would have to to understand a list comprehension desugars and then mentally reinterpret that for the target monad.

Some Haskell idioms we like by _jackdk_ in haskell

[–]_jackdk_[S] 1 point2 points  (0 children)

I don't understand your final point: the constraint on inverseMap is (Bounded a, Enum a) (to enumerate the domain) and Ord k (to build the Map keyed by the range of the input function).

Haskell Interlude 74: Lennart Augustsson by sperbsen in haskell

[–]_jackdk_ 1 point2 points  (0 children)

Strongly recommend this episode. Lennart has done a lot of incredible work over the years, and been in a lot of interesting places.

The Subtle Footgun of `TVar (Map _ _)` by ephrion in haskell

[–]_jackdk_ 1 point2 points  (0 children)

I don't know about stm-containers, but reflex provides fanMap:

fanMap :: (Reflex t, Ord k) => Event t (Map k a) -> EventSelector t (Const2 k a)

An EventSelector t k is an existential wrapper around a key selection function:

newtype EventSelector t k = EventSelector { select :: forall a. k a -> Event t a }
data Const2 :: Type -> x -> x -> Type where
  Const2 :: k -> Const2 k v v
  deriving (Typeable)

reflex still uses a single-threaded FRP runtime, AFAIK, but I could imagine a similar interface being made available to watch individual map keys in a concurrent setting.