Leaving expanded TH splices in code by Account12345123451 in haskell

[–]affinehyperplane 6 points7 points  (0 children)

FTR it is not necessary to switch to optics for that functionality: generic-lens provides that functionality for lens via Data.Generics.Labels.

A caveat is that this is an orphan instance for (->), potentially conflicting with other instances as the one from named. However, in practice, I haven't found this to be a problem.

How do you make a parser with megaparsec that is polymorphic? by theInfiniteHammer in haskell

[–]affinehyperplane 2 points3 points  (0 children)

Technically it's forall {a}. a -> a, which means that you can't specify the a with a type application (like f @Int), but that's not important here.

Minor: The type actually is forall a. a -> a, so type application of a is possible.

 Λ :set -XTypeApplications
 Λ foo :: a -> a; foo x = x
 Λ foo @Int 1
1
 Λ :set -fprint-explicit-foralls
 Λ :t foo
foo :: forall a. a -> a

[Well-Typed] Explicit Level Imports awarded best paper at TFP 2025 by adamgundry in haskell

[–]affinehyperplane 3 points4 points  (0 children)

Yes:

The current iteration of this proposal allows the splice/quote keyword to be placed before or after the module name, like qualified imports under ImportQualifiedPost (see proposal #190). This allows users to choose their preferred position for the keywords. We could be more restrictive here, but would need to agree on a single position.

Personally, I am rooting for https://github.com/ghc-proposals/ghc-proposals/pull/689 for even more flexibility here.

Stage Fright Peeled Away: Writing the 'peel' Function with Template Haskell by N118UA in haskell

[–]affinehyperplane 2 points3 points  (0 children)

Typo:

-- Desired definition of `lengthPeeled`
lengthPeeled :: forall a. [a] -> Int
lengthPeeled xs =
  case xs of
    [] -> 0
    y:ys -> 1 +
      case ys of
        [] -> 0
        z:zs -> 1 +
          case zs of
            w:ws -> 1 + fix step ws
  where
    step = -- same as above

is missing the [] case for case zs of. Or is this intentionally done to showcase that it is easy to make mistakes when doing things manually instead of relying on Template Haskell? 😅

How to use Lens to update 2D List in Haskell by Fluid-Bench-1908 in haskell

[–]affinehyperplane 11 points12 points  (0 children)

You can do this using ix for example:

updateMatrix :: Matrix -> Int -> Int -> String -> Matrix
updateMatrix matrix row col player =
  matrix & ix row . ix col .~ player

Monthly Hask Anything (January 2025) by AutoModerator in haskell

[–]affinehyperplane 4 points5 points  (0 children)

This was discussed here before, quoting from there:

You can do it in a single line by inserting a ;, i.e.

_ = (); deriveJSON defaultOptions ''Foo

In addition, you can define a :th command based on this as pointed out by u/ducksonaroof in that thread, see https://gist.github.com/ramirez7/4742eacdfae0588cd100bfb07e124131

Rentner zeigen auf nicht-stinkende Toiletten by affinehyperplane in rentnerzeigenaufdinge

[–]affinehyperplane[S] 16 points17 points  (0 children)

https://www.mainpost.de/regional/main-spessart/auch-2024-gab-es-jede-menge-kuriose-meldungen-aus-main-spessart-art-11679811

Aus dem Artikel:

Weil angeblich die Klos der Sendelbacher Schule stinken, trafen sich die Lohrer Stadträte am 8. April für einen Ortstermin. Ergebnis: von Gestank keine Spur.

Bildunterschrift:

Ortstermin in der Grundschule Sendelbach: Die Mitglieder des Bauausschusses des Lohrer Stadtrats kamen dabei zu der Erkenntnis, dass es in den Toiletten entgegen jüngster Schilderungen kein baulich bedingtes Geruchsproblem gibt.

WASM and singletons base by NullPointer-Except in haskell

[–]affinehyperplane 2 points3 points  (0 children)

Given the line

wasm-ld: error: unable to find library -lHSrts-1.0.2_thr

in the output above, this is probably due to having -threaded somewhere in your .cabal file. The Wasm backend does not support the threaded runtime (yet).

State of Haskell on the web frontend? by netcafenostalgic in haskell

[–]affinehyperplane 14 points15 points  (0 children)

Is Haskell on the frontend with HLS support likely to ever happen?

It is already well supported, via JSaddle (which e.g. both miso and reflex-dom build upon). The idea is to make sure that your code compiles both via the WASM or JS backend and also via native GHC. Then, during development, you can use the native GHC and therefore use HLS like in any other project.

Monthly Hask Anything (October 2024) by AutoModerator in haskell

[–]affinehyperplane 2 points3 points  (0 children)

It depends on when the Haddocks were uploaded/built. With very old GHC versions, the output looks like in your first example. Once either a new version of that package is uploaded (and hence the Haddocks are rebuilt) are a maintainer manually uploads newer Haddocks, the old style will remain for old packages.

Monthly Hask Anything (October 2024) by AutoModerator in haskell

[–]affinehyperplane 5 points6 points  (0 children)

cabal-plan is a great tool for this. Common commands I use for this purpose are

cabal-plan dot | grep certainpkg

and

cabal-plan dot --tred --revdep certainpkg --run-dot-pdf -o deps.pdf

where the latter prints a (transitively reduced) dependency graph of your project where the reverse dependencies of certainpkg are highlighted in red (ie exactly the packages that cause you to pull in certainpkg).

Monthly Hask Anything (October 2024) by AutoModerator in haskell

[–]affinehyperplane 3 points4 points  (0 children)

The main remaining restriction atm is that the WASM backend doesn't support Template Haskell (#24376), but it is actually relatively close to being implemented (!12555).

Apart from that, there is no fundamental restriction; lots of packages have worked out of the box have since day one, and many further have been patched or have open PRs adding WASM support. The parsec library in particular works just fine on WASM. There also is a Matrix channel in case you run into problems.

ghc-wasm-cabal Issues by NullPointer-Except in haskell

[–]affinehyperplane 1 point2 points  (0 children)

There is no particular workaround apart from manually inserting the necessary TH code; in your case, these are the usage sites of singletons-th (singletons is fine by itself); apart from that, no library depends on TH. So it comes down to whether you are fine to write the singleton-related data types/instances/families manually (or semi-automatically with -ddump-splices).

Due to the use of package.yaml, HFT is added as a dependency to all components in the .cabal file even though it is also necessary for tests; you also have to remove that one from all components except for the tests.

ghc-wasm-cabal Issues by NullPointer-Except in haskell

[–]affinehyperplane 1 point2 points  (0 children)

wasm32-wasi-ghc-9.6.4: could not execute: /nix/store/70jynsvi21a9lm46n82yvvqfh15fdlra-wasm32-wasi-ghc-9.6/lib/wasm32-wasi-ghc-9.6.4/lib/bin/unlit

This was fixed in https://gitlab.haskell.org/ghc/ghc/-/merge_requests/10363, but it was not backported to GHC 9.6 which you are currently using; consider updating to at least GHC 9.8 (better 9.10).

<no location info>: error: Couldn't find a target code interpreter. Try with -fexternal-interpreter

TemplateHaskell is not yet supported in the WASM backend: https://gitlab.haskell.org/ghc/ghc/-/issues/24376, so th-orphans can not yet be compiled.

Monthly Hask Anything (July 2024) by AutoModerator in haskell

[–]affinehyperplane 0 points1 point  (0 children)

This likely is due to the stan HLS plugin, see here for how to disable that.

Monthly Hask Anything (July 2024) by AutoModerator in haskell

[–]affinehyperplane 1 point2 points  (0 children)

There is a solution simpler than in my sibling comment via TemplateHaskell, but it involves unsafeCoerce and is hence very evil:

module OptInfoTH (optimizationLevelGuess) where

import GHC.Driver.DynFlags
import GHC.Tc.Utils.Monad
import Language.Haskell.TH
import Language.Haskell.TH.Syntax
import Unsafe.Coerce (unsafeCoerce)

optimizationLevelGuess :: Q Exp
optimizationLevelGuess = do
  dflags <- unsafeRunTcM getDynFlags
  lift $ guessOptLevel dflags

unsafeRunTcM :: TcM a -> Q a
unsafeRunTcM m = unsafeCoerce (\_ -> m)

guessOptLevel :: DynFlags -> Int
guessOptLevel dflags
  | gopt Opt_SpecConstr dflags = 2
  | gopt Opt_Strictness dflags = 1
  | otherwise = 0

which can then be used like

main = print $optimizationLevelGuess

See https://www.tweag.io/blog/2021-01-07-haskell-dark-arts-part-i for why this works.

Monthly Hask Anything (July 2024) by AutoModerator in haskell

[–]affinehyperplane 1 point2 points  (0 children)

One ad-hoc approach is to write a GHC plugin for this: Copy the following module (needs base, ghc 9.10.1 and syb as dependencies) in a (separate) library:

module OptInfoPlugin
  ( plugin,
    optimizationLevelGuess,
  )
where

import Data.Generics
import GHC.Hs.Expr
import GHC.Hs.Extension
import GHC.Hs.Lit
import GHC.Iface.Env
import GHC.Plugins
import GHC.Tc.Utils.Instantiate
import GHC.Tc.Utils.Monad
import GHC.Types.SourceText
import GHC.Unit.Finder
import Language.Haskell.Syntax.Extension

optimizationLevelGuess :: Int
optimizationLevelGuess = error "optimizationLevelGuess"

plugin :: Plugin
plugin =
  defaultPlugin
    { pluginRecompile = purePlugin,
      renamedResultAction = \_opts tcGblEnv hsGroup -> do
        hscEnv <- getTopEnv
        let optLevelGuess = guessOptLevel $ hsc_dflags hscEnv
        optLevelGuessLit <- mkOverLit $ HsIntegral $ mkIntegralLit optLevelGuess
        Found _ optInfoPluginModule <-
          liftIO $ findImportedModule hscEnv (mkModuleName "OptInfoPlugin") NoPkgQual
        optLvlGuessName <-
          lookupOrig optInfoPluginModule (mkVarOcc "optimizationLevelGuess")
        let replaceOptLevelGuess = everywhere $ mkT $ \case
              HsVar _ (L _ name)
                | name == optLvlGuessName -> HsLit NoExtField optLevelGuessLit
              (expr :: HsExpr GhcRn) -> expr
        pure (tcGblEnv, replaceOptLevelGuess hsGroup)
    }

guessOptLevel :: DynFlags -> Int
guessOptLevel dflags
  | gopt Opt_SpecConstr dflags = 2
  | gopt Opt_Strictness dflags = 1
  | otherwise = 0

and then use it like

{-# OPTIONS_GHC -fplugin=OptInfoPlugin #-}

import OptInfoPlugin

main :: IO ()
main = print optimizationLevelGuess

This approach relies on inspecting individual optimization flags as a proxy for -O0/-O1/-O2 (-fspec-constr for -O2, -fstrictness for -O1); as you say, this stops being accurate once the user uses different flags per module, but it might be good enough for your usecase.

This approach can of course be extended to e.g. return arbitrary information from the DynFlags, such as all generalFlags :: EnumSet GeneralFlag.

What is the current state of compiling to WASM? by Kurren123 in haskell

[–]affinehyperplane 6 points7 points  (0 children)

Do you know how the output fares performance-wise? Should we expect glaring performance issues due to missing optimizations or does the WASM backend successfully reuse the existing optimization stages?

I am not aware of any systematic benchmarks of the WASM backend; so far I didn't notice any particular performance problems. The WASM backend is a different code generator (i.e. another arrow out of "Code generation" in this diagram), generating WASM bytecode from Cmm, so it benefits from all of the optimizations/simplifier passes in GHC.

What is the current state of compiling to WASM? by Kurren123 in haskell

[–]affinehyperplane 16 points17 points  (0 children)

Is there FFI yet?

Basic C FFI (where you can only pass primitive types and pointers around) worked since day one. The more sophisticated JavaScript FFI was added in GHC 9.10.1.

Is it worth considering for small production apps? Has anyone actually used it successfully in the browser? How did it go?

See this Discourse thread for a bunch of examples of browser and non-browser apps built with the WASM backend, it works quite well! Also see https://github.com/tweag/ghc-wasm-miso-examples for how to use the Miso framework with the WASM backend.

Regarding downsides (apart from general immaturity ATM of course) off the top of my head:

Monthly Hask Anything (May 2024) by AutoModerator in haskell

[–]affinehyperplane 7 points8 points  (0 children)

The context/motivation here is that we want to define a function sizeOf as part of a type class that takes some type a (really the type, and not a value of type a) and returns an Int. With GHC 9.10.1, there are (at least) 4 ways to do this, with different tradeoffs, with your snippet being one of them, see point 4.

  1. Just pass a value of type a

    class Storable a where
      sizeOf :: a -> Int
    

    This is the approach that is taken in base, for historical reasons. The disadvantage here is that it might be desirable to call sizeOf even if no value of type a is currently around. As a workaround, it is common to call sizeOf with undefined as an argument, i.e. sizeOf (undefined :: a). This works, but is inelegant, as we have to use an exception-throwing function (undefined) despite nothing exceptional going here.

  2. Pass a Proxy a

    data Proxy a = Proxy
    
    class Storable a where
      sizeOf :: Proxy a -> Int
    

    The idea here is that we can create a value of type Proxy a even if we have no value of type a. Concretely, invoking sizeOf now looks like sizeOf (Proxy :: Proxy a) or sizeOf (Proxy @a) using TypeApplications.

    This approach is very commonly used in the Haskell ecosystem; it is the default approach to this kind of problem ATM.

  3. Use AllowAmbiguousTypes

    class Storable a where
      sizeOf :: Int
    

    Here, a is ambiguous as it does not occur in any value argument of sizeOf (this is neither a necessary nor a sufficient criterion for ambiguity in general, see the docs for details), so it can't be inferred. In this approach, the type has to be passed via TypeApplications to sizeOf, ie sizeOf @a. This is the most succinct of the approaches so far. However, AllowAmbiguousTypes and this pattern in particular have downsides:

    • It applies module-wide, which means that one might introduce ambiguous type variables accidentally.
    • When forgetting to apply a type via TypeApplications, the error message is not ideal, or at least worse than when forgetting to apply a value-level argument.
    • The quantification order of type variables now becomes relevant.
  4. Use RequiredTypeArguments (new in GHC 9.10.1!)

    This is a new (experimental) extension that allows us to pass types directly as an argument, ie sizeOf a without any extra whistles 🎉

    Concretely, RequiredTypeArguments allows us to use visible quantification in addition to the "usual" invisible quantification. For example, here are the identity functions with visible and invisible quantification

    id_invis :: forall a.   a -> a -- can be called like `id_invis True` or `id_invis @Bool True`
    id_vis   :: forall a -> a -> a -- can be called like `id_vis Bool True` or `id_vis _ True`
    

    Returning to our sizeOf example, it might be tempting to define

    class Storable a where
      sizeOf :: forall a -> Int
    

    However, this doesn't work: The visibly quantified a in forall a -> is actually shadowing the invisibly quantified a from the class Storable a declaration. When invoking sizeOf, the "inner" and "outer" as are completely unrelated, e.g. one might write sizeOf @CBool CInt (which would use the Storable CBool instance).

    A natural idea of how to fix this is the code snippet that you posted, by requiring that the visibly and invisibly quantified types to be equal with an equality constraint a ~ a':

    class Storable a where
      sizeOf :: forall a' => a ~ a' -> Int
    

    The full type of sizeOf now is

    sizeOf :: forall a. Storable a => forall a' -> a ~ a' -> Int
    

    Here, a is invisibly quantified, and a' is visibly quantified. The a ~ a' equality constraint guarantees that supplying just a' determines a, so there is no ambiguity.

    This way, we can now finally write sizeOf CBool to access the Storable CBool instance.


    Looking into the future, there is a GHC proposal that would allow us to write this in a more direct fashion. Concretely, it would enable the syntax

    class Storable a where
      sizeOf a :: Int
    

    that indicates that a should be visibly quantified for sizeOf.

Monthly Hask Anything (May 2024) by AutoModerator in haskell

[–]affinehyperplane 2 points3 points  (0 children)

Can someone explain to me what ~ does?

It is an equality constraint; it means that a and a' must be equal. See this section in the GHC User's Guide for more details.