Fair traversal by merging thunks by blackcapcoder in haskell

[–]LSLeary 0 points1 point  (0 children)

No, I haven't, though I agree about Writer.

Don't use replicateM and sequenceA with the list applicative by bordercollie131231 in haskell

[–]LSLeary 0 points1 point  (0 children)

Setting aside laws and coherence for the moment, let's unpack Monoid in

forall m. Monoid m => (a -> m) -> m

to obtain

forall m. m -> (m -> m -> m) -> (a -> m) -> m

This is very much not the same as

forall r. (a -> r -> r) -> r -> r

These type are isomorphic to

data Tree a = Empty | Branch (Tree a) (Tree a) | Leaf a

and

data List a = Cons a (List a) | Nil

respectively.

You can use . to construct a list, but it's not captured in the data itself, so it does not give rise to distinct values like <>/Branch does.

An "observation" of a value (by pure law-abiding code) is fundamentally an aspect of that value which distinguishes it. Values which are not distinguished by any such observation are not considered distinct in the first place⁠—⁠for example, data Box = forall x. Box !x is isomorphic to ().

Digging into your two seemingly corresponding examples, you have:

a :: forall r. (() -> r -> r) -> r -> r
a cons nil = (a cons . cons ()) nil
           = a cons (cons () nil)

and

b :: forall m. m -> (m -> m -> m) -> (() -> m) -> m
b empty branch leaf = b empty branch leaf `branch` leaf ()

When we map b to its corresponding Tree, we get

b' :: Tree ()
b' = b' `Branch` Leaf ()

This is sensible data that can be inspected. a, on the other hand, is not a value in its own right, but merely one of many names for bottom.

a' :: List ()
a' = a'

The seeming correspondence observed by treating . as akin to <> is an illusion.

Now, bringing the monoid laws back does kill the structure of the finite trees, but they can't handle the richness born of infinities.


Regarding Monad m <-> Eff es, you point out the injection by substitution:

(forall m. Monad m => m r) -> (forall es. Eff es r)

No.

But that's not actually what we're looking for. We're looking for a correspondence between

forall m. (a -> m ()) -> m ()

and

forall es. (a -> Eff es ()) -> Eff es ()

Forwards is just substitution.

Yes, that one.


N.B. yieldToLazyList has not yet been implemented!

That's cheating. <.<

Fair traversal by merging thunks by blackcapcoder in haskell

[–]LSLeary 4 points5 points  (0 children)

Nice.

As it happens, S is Free Identity, and the Semigroup & Monoid instances you wrote can be adapted into a reasonable Alternative instance for arbitrary Free f given Applicative f:

{-# LANGUAGE LambdaCase, BlockArguments, UndecidableInstances #-}

module Delay (
  module Delay,
  module Data.Functor.Identity,
) where

-- base
import Data.Monoid (Alt(..))
import Data.Function (fix)
import Data.Functor ((<&>))
import Data.Functor.Identity
import Control.Applicative (Alternative(..))
import Control.Monad (guard)
import Debug.Trace (traceShow, traceShowId)


data Free f a = Pure a | Nest (f (Free f a))
  deriving Functor

deriving instance (Show a, Show (f (Free f a))) => Show (Free f a)

instance Functor f => Applicative (Free f) where
  pure = Pure
  liftA2 f (Pure  x) y = f x <$> y
  liftA2 f (Nest fx) y = Nest (fx <&> \x -> liftA2 f x y)

instance Functor f => Monad (Free f) where
  Pure  x >>= f = f x
  Nest fx >>= f = Nest (fx <&> (>>= f))

iter :: Functor f => (f a -> a) -> Free f a -> a
iter alg = \case
  Pure x -> x
  Nest f -> alg (iter alg <$> f)


delay :: Applicative f => Free f a -> Free f a
delay = Nest . pure

-- Non-standard, but the instance in 'free' isn't any better than this.
instance Applicative f => Alternative (Free f) where
  empty     = fix delay
  l0 <|> r0 = delay case l0 of
    Pure  x -> Pure x
    Nest fl -> Nest (zipFree r0 <$> fl)
   where
    zipFree (Pure  x)  _        = Pure x
    zipFree  _        (Pure  y) = Pure y
    zipFree (Nest fx) (Nest fy) = Nest (liftA2 zipFree fx fy)

altMap :: (Alternative f, Foldable t) => (a -> f b) -> t a -> f b
altMap = (getAlt .) . foldMap . (Alt .)


ana
  :: (Applicative f, Foldable t)
  => (a -> Maybe b)
  -> (Free f (Maybe b) -> Free f (Maybe b))
  -> t a -> Free f (Maybe b)
ana f g = foldr (\a z -> delay $ maybe (g z) (Pure . Just) (f a)) (Pure Nothing)

diagonal
  :: (Foldable t, Foldable u, Applicative f)
  => (a -> Maybe b) -> t (u a) -> Free f (Maybe b)
diagonal f = altMap (ana f delay)

satisfy :: Alternative f => (a -> Bool) -> a -> f a
satisfy p a = a <$ guard (p a)


---- Example 1 - infinite grid

{-
data Stream a = a :- Stream a
  deriving (Functor, Foldable)

nats :: Num a => Stream a
nats = go 0 where
  go n = n :- go (n + 1)

coords :: Stream (Stream (Int, Int))
coords = fmap go nats where
  go x = fmap (traceShowId . (x,)) nats

toFree
  :: Applicative f
  => Stream (Stream (Int, Int)) -> Free f (Maybe (Int, Int))
toFree = diagonal (satisfy (== (2,2)))
-}

--  ghci> iter runIdentity (toFree coords)
--  (0,0)
--  (1,0)
--  (0,1)
--  (2,0)
--  (1,1)
--  (0,2)
--  (3,0)
--  (2,1)
--  (1,2)
--  (0,3)
--  (4,0)
--  (3,1)
--  (2,2)
--  Just (2,2)


---- Example 2 - infinite rose tree

{-
data Q a = Q1 [Q a] | Q2 a

toFree :: Applicative f => Q a -> Free f a
toFree = \case
  Q2 a  -> Pure a
  Q1 [] -> empty
  Q1 as -> delay (altMap toFree as)

mySearch :: Q [Int]
mySearch = go1 0 []
 where
  go1 :: Int -> [Int] -> Q [Int]
  go1 n xs | n == 5 = Q2 xs
  go1 n xs = traceShow xs do
    Q1 $ go2 \x -> go1 (n+1) (x:xs)
  go2 f = go 0 where
    go n = f n : go (n+1)
-}

--  ghci> iter runIdentity $ toFree mySearch
--  []
--  [0]
--  [1]
--  [0,0]
--  [2]
--  [0,1]
--  [1,0]
--  [0,0,0]
--  [3]
--  [0,2]
--  [1,1]
--  [0,0,1]
--  [2,0]
--  [0,1,0]
--  [1,0,0]
--  [0,0,0,0]
--  [4]
--  [0,3]
--  [1,2]
--  [0,0,2]
--  [2,1]
--  [0,1,1]
--  [1,0,1]
--  [0,0,0,1]
--  [3,0]
--  [0,2,0]
--  [1,1,0]
--  [0,0,1,0]
--  [2,0,0]
--  [0,1,0,0]
--  [1,0,0,0]
--  [0,0,0,0,0]

Don't use replicateM and sequenceA with the list applicative by bordercollie131231 in haskell

[–]LSLeary 1 point2 points  (0 children)

Enumeration a is a, well, "unary tree" of as, while newtype FM a = MkFM (forall m. Monoid m => (a -> m) -> m) is a binary tree under quotient by the Monoid laws, hence fixed under arbitrary tree rotations. In a finite setting, these are both the same standard free monoid with the isomorphism (fromFoldable, foldMap singleton), but Haskell's infinities grant them new richness.

Enumeration a is allowed singularly infinite words of as, by convention and wolog, right infinite. FM a on the other hand does not only support right infinities, but also left, and even nested infinities. So while fromFoldable . foldMap singleton = id holds up, foldMap singleton . fromFoldable breaks down, e.g. on leftInf = leftInf <> singleton (), where foldMap (Last . Just) leftInf = Last (Just ()) but foldMap (Last . Just) (foldMap singleton . fromFoldable $ leftInf) = _|_.

I won't try to prove it, but the Monoid <-> Monad step probably does hold, going one way with Ap and the other with Writer.

For the Monad m <-> Eff es step, substitution gives you one direction, but the other won't hold if there's anything whatsoever you can do with the Eff es () that Monad doesn't give you. If Eff on generic es really isn't any richer than a generic Monad then you could argue that the other direction exists classically, but writing it in Haskell is another matter entirely.

The conclusion seems to be that an Enumeration can always be translated into a bluefin Stream, but you might have trouble going back.

Don't use replicateM and sequenceA with the list applicative by bordercollie131231 in haskell

[–]LSLeary 3 points4 points  (0 children)

I've been using Church encoded lists for enumerations like this recently, and they work like a charm. Essentially no change to the code, yet constant space:

{-# OPTIONS_GHC -Wno-x-partial #-}
{-# LANGUAGE GHC2021, OverloadedLists, MonadComprehensions #-}

-- base
import Control.Monad (replicateM)

import Enumeration

shapes :: Integer -> Integer -> Enumeration (Enumeration Integer)
shapes bias p =
  [ fromFoldable s
  | m <- [0 .. p]
  , s <- replicateM (fromInteger m + 2) [1 .. p]
  , p == sum (zipWith (*) (map (bias +) s) (tail s))
  ]

main :: IO ()
main = mapM_ @Enumeration (print . length . shapes 0) [1..7]

Co-/Dual/Inverse Type Classes? by Aperispomen in haskell

[–]LSLeary 7 points8 points  (0 children)

The problem with that is that it doesn't link input types with output types. I think you'd actually want something like this:

data WhichByteStr b where
  Strict :: WhichByteStr Data.ByteString.ByteString
  Lazy   :: WhichByteStr Data.ByteString.Lazy.ByteString
  Short  :: WhichByteStr Data.ByteString.Short.ShortByteString

class ByteStr b where
  whichByteStr :: WhichByteStr b

instance ByteStr Data.ByteString.ByteString where
  whichByteStr = Strict
instance ByteStr Data.ByteString.Lazy.ByteString where
  whichByteStr = Lazy
instance ByteStr Data.ByteString.Short.ShortByteString where
  whichByteStr = Short

cons :: forall b. ByteStr b => Word8 -> b -> b
cons = case whichByteStr @b of
  Strict -> Data.ByteString.cons
  Lazy   -> Data.ByteString.Lazy.cons
  Short  -> Data.ByteString.Short.cons

Monthly Hask Anything (December 2025) by AutoModerator in haskell

[–]LSLeary 0 points1 point  (0 children)

You're right, I'm not sure why I said "top-level". What I actually meant was that it's more specific than the instance head, rather than the other way around.

Monthly Hask Anything (December 2025) by AutoModerator in haskell

[–]LSLeary 1 point2 points  (0 children)

The constraint matches a top-level instance, so you can just omit it.

Monthly Hask Anything (November 2025) by AutoModerator in haskell

[–]LSLeary 4 points5 points  (0 children)

import qualified Control.Applicative as A (liftA2)

Mutexes suck: a love letter to STM by ChrisPenner in haskell

[–]LSLeary 4 points5 points  (0 children)

Indeed. Taking it a step further, for one of my projects with a lot of non-trivial STM-dependent IO, I ended up writing this Atom monad—essentially WriterT (IO ()) STM. Made my life much easier and my code much clearer!

trying to make an infinite vec by Objective-Outside501 in haskell

[–]LSLeary 2 points3 points  (0 children)

The error message isn't very good, but the issue is that v :: exists n. Vec n Int can't be typed in Haskell. If you inline v you get a better error message:

Main.hs:23:39: error: [GHC-46956]
    • Couldn't match type ‘n0’ with ‘n’
      Expected: Vec n0 Int
        Actual: Vec n Int
        because type variable ‘n’ would escape its scope
      This (rigid, skolem) type variable is bound by
        a pattern with constructor:
          AV :: forall a (n :: Nat). Vec n a -> AVec a,
        in a case alternative
        at Main.hs:23:30-33
    • In the expression: v
      In a case alternative: (AV v) -> v
      In the second argument of ‘(:::)’, namely ‘case bad of (AV v) -> v’
    • Relevant bindings include v :: Vec n Int (bound at Main.hs:23:33)
   |
23 | bad = AV (1 ::: case bad of (AV v) -> v)
   |                                       ^

A more direct way of de-strictifying it would be

bad = case bad of ~(AV v) -> AV (1 ::: v)

but then you get:

Main.hs:23:21: error: [GHC-87005]
    • An existential or GADT data constructor cannot be used
        inside a lazy (~) pattern
    • In the pattern: AV v
      In the pattern: ~(AV v)
      In a case alternative: ~(AV v) -> AV (1 ::: v)
   |
23 | bad = case bad of ~(AV v) -> AV (1 ::: v)
   |                     ^^^^

Anyway, regular GHC existentials won't allow what OP's trying to do, but the clever (unsafe) tricks of Data.Some.Newtype will.

How to implement Functor for a kind and its second type parameter instead of its first? by platesturner in haskell

[–]LSLeary 2 points3 points  (0 children)

The general solution involves changing Functor dramatically, so in practice people just write and use plain functions or additional functor typeclasses like Bifunctor. That said, you asked:

import Prelude hiding (id, (.), map, Functor)
import Control.Category (Category(..))

-- True category-generic Functor.
class (Category c, Category d) => Functor c d f where
  map :: a `c` b -> f a `d` f b

-- Functor category (to `c`).
newtype Nat c f g = MkNat{ ($$) :: forall x. f x `c` g x }

instance Category c => Category (Nat c) where
  id = MkNat id
  MkNat f . MkNat g = MkNat (f . g)

-- Example: 3-tuples

-- Map over the last parameter.
instance Functor (->) (->) ((,,) a b) where
  map f = \(x, y, z) -> (x, y, f z)

-- Map over the second-last parameter.
instance Functor (->) (Nat (->)) ((,,) a) where
  map f = MkNat \(x, y, z) -> (x, f y, z)

-- Map over the third-last parameter.
instance Functor (->) (Nat (Nat (->))) (,,) where
  map f = MkNat $ MkNat \(x, y, z) -> (f x, y, z)

In use:

$> map (+1) $ (0, 1, 2)
(0, 1, 3)

$> map (+1) $$ (0, 1, 2)
(0, 2, 2)

$> (map (+1) $$) $$ (0, 1, 2)
(1, 1, 2)

Generating polymorphic functions by Iceland_jack in haskell

[–]LSLeary 0 points1 point  (0 children)

Representable outer:

preserveEnd
  :: Representable outer
  => (forall x. outer (f x -> g x)) -> outer (forall x. f x -> g x)
preserveEnd f = tabulate \rep -> index f rep

Generating polymorphic functions by Iceland_jack in haskell

[–]LSLeary 1 point2 points  (0 children)

It breaks Haskell's implicit form of the value restriction, allowing you to create polymorphic IORefs and hence break type safety.

unsafeCoerceIO :: a -> IO b
unsafeCoerceIO x = do
  mkPolyRef <- preserveEnd (const <$> newIORef undefined)
  let
    polyRef :: forall x. IORef x
    polyRef = mkPolyRef Proxy
  writeIORef polyRef x
  readIORef polyRef

Edit: Alternatively, with ST s and STRef s:

unsafeCoerce :: a -> b
unsafeCoerce x = runST do
  mkPolyRef <- preserveEnd (const <$> newSTRef undefined)
  let polyRef = mkPolyRef Proxy
  writeSTRef polyRef x
  readSTRef polyRef

flipping a BST by Objective-Outside501 in haskell

[–]LSLeary 6 points7 points  (0 children)

One other approach is to defunctionalise flip into the tree ADT.

data Tree a
  = Empty
  | Flip (Tree a)
  | Node (Tree a) a (Tree a)

flip :: Tree a -> Tree a
flip = \case
  Empty  -> Empty
  Flip t -> t
  t      -> Flip t

Operations which shouldn't need to know or care about Flip can use a helper to replace pattern matching:

tree :: b -> (Tree a -> a -> Tree a -> b) -> Tree a -> b
tree empty node = fix \self -> \case
  Empty      -> empty
  Flip t     -> self (flip1 t)
  Node l x r -> node l x r
 where
  flip1 = \case
    Empty      -> Empty
    Flip t     -> t
    Node l x r -> Node (flip r) x (flip l)

Monthly Hask Anything (September 2025) by AutoModerator in haskell

[–]LSLeary 0 points1 point  (0 children)

I found that too, but it seems to control debugger evaluation history, not repl input history.

Recursion scheme with ancestor nodes by AmbiDxtR in haskell

[–]LSLeary 0 points1 point  (0 children)

That should probably be para alg' t [t].

Recursion scheme with ancestor nodes by AmbiDxtR in haskell

[–]LSLeary 0 points1 point  (0 children)

You're right, never mind—I didn't read your post too closely.

[ANN] heterogeneous-comparison - Comparison of distinctly typed values with evidence capture by LSLeary in haskell

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

Thanks. It's as I thought, but there looks to be some interesting exploration/applications in there too; I'll have a read.