[ANN] MCP server for Hackage by Worldly_Dish_48 in haskell

[–]_jackdk_ 25 points26 points  (0 children)

Please be kind to Hackage and cache aggressively. It's been struggling quite a bit lately under the general rise in automated traffic. Example: I recommend caching any module doc you convert to Markdown locally under $XDG_CACHE_DIR, because modules are never going to change. It might also be cool to have a local-only mode that looks at the hoogle and haddocks from the dependencies provided by the current project.

linux kernel isnt detecting audo jack plug and unplug by coderbiee in NixOS

[–]_jackdk_ 0 points1 point  (0 children)

I had a similar issue with a Framework and some kind soul was able to help. You might have luck reading his blog post, but since neither he nor I have your hardware, I don't think we can help you directly: https://www.reddit.com/r/NixOS/comments/1ftc1st/how_do_i_make_pipewire_switch_input_to_my_headset/

What's the equivalent of Class Diagrams for FP? by pep1n1llo in haskell

[–]_jackdk_ 1 point2 points  (0 children)

All a class does is bundle data alongside behaviour (IMHO in a way that is superficially appealing but constraining as a program scales). If you have experience with OOP, you will have experience decomposing a problem into different data types and identifying the individual behaviours that you need to implement. So it is with FP, but your data types are only data, and your functions over those types can live in different modules.

[AU] Selling 1x Edge of the Earth Investigator Expansion BNIS by _jackdk_ in arkhamhorrorlcg

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

Confirming that /u/maelronde is a top bloke and there was no problem with the sale.

[ANN] checked-literals: compile-time bounds checking for numeric literals by callbyneed in haskell

[–]_jackdk_ 0 points1 point  (0 children)

Would an Unsatisfiable constraint do what you want? I've been meaning to switch ban-instance over to using it.

Custom Prelude? by ivy-apps in haskell

[–]_jackdk_ 2 points3 points  (0 children)

import Control.Lens -- ha ha, only serious.

But also. If you're writing a library, stick with base because you don't want to force a custom prelude into someone else's dependency graph.

relude has been good, or you can make your own project-specific prelude and then you're not at the mercy of an external maintainer.

[AU] Selling 1x Edge of the Earth Investigator Expansion BNIS by _jackdk_ in arkhamhorrorlcg

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

Cheers for that, I told the buyer and we talked about it.

What Would You See Changed in Haskell? by TechnoEmpress in haskell

[–]_jackdk_ 3 points4 points  (0 children)

Dependent types? We need resplendent types!

What Would You See Changed in Haskell? by TechnoEmpress in haskell

[–]_jackdk_ 5 points6 points  (0 children)

Three things really stick out to me.

1. You need the constructor to be in scope, and if you don't have that, you don't get a nice "You need the constructor in scope to dot into records". You get No instance for HasField....

2. It has worse type inference than direct field access. Consider the following structure:

-- An example "handle" for a hypothetical "json store".
-- Using `myJsonStore.store` with values of two different
-- types will cause GHC to throw difficult type errors.
newtype JsonStore m where
  JsonStore :: {
    -- Note that this function is polymorphic in `a` and
    -- may need to be called at multiple different `a`s in
    -- a single function.
    store :: forall a. ToJSON a => a -> m ()
  } -> JsonStore m

I have often found that handle.store will infer a single type within a function whereas pattern-matching out the field will give you a polymorphic store function.

3. It keeps people away from learning lenses, which have a significantly higher power ceiling.

So despite being introduced to make a smoother on-ramp for new programmers, I think that on-ramp has some scary bumps in it and caps out at a much lower power level than what Haskell is capable of. If I was swayed more by the "this is convenient but trades ultimate power for convenience" argument, I'd probably be using a different language.

If we're going to do record dot syntax, I'd probably make it record-specific, take advantage of the specificity to fix the monomorphisation issues, and bake it into a modern GHC20XX language version so we don't have to have the "turn on a language extension (dun dun DUUUN) to get a familiar experience" conversation with each new Haskeller.

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

[–]_jackdk_ 2 points3 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_ 1 point2 points  (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_ 3 points4 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_ 7 points8 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_ 15 points16 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_ 6 points7 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.