0

With the following code insertRnd works properly but I can't get scramble or anything else that uses insertRnd to compile:

scramble :: ∀ eff. String -> Eff (random :: RANDOM | eff) String
scramble s = foldl insertRnd (take 1 s) (drop 1 s)

insertRnd :: ∀ eff. String -> String -> Eff (random :: RANDOM | eff) String
insertRnd st ch = do
  n <- randomInt 0 $ length st
  pure $ insertAt n ch st

I get the following error message

Could not match type

Eff
  ( random :: RANDOM
  | t2
  )
  String

with type

String

while checking that type forall eff.
                       String
                       -> String
                          -> Eff
                               ( random :: RANDOM
                               | eff
                               )
                               String
is at least as general as type t0 -> t1 -> t0
while checking that expression insertRnd
has type t0 -> t1 -> t0
in value declaration scramble

where t0 is an unknown type
  t1 is an unknown type
  t2 is an unknown type

What am I doing wrong?

1 Answers1

0

First, your insertRnd itself doesn't compile for me, because there is no function insertAt :: Int -> String -> String -> String (or even Int -> Char -> String -> String, see below). There is such function for arrays and lists and some other stuff, but not for strings. I will just assume here that you wrote such function yourself and it exists in your code.

Second, even if it does compile, it has wrong signature for a fold: you're trying to fold over a String, which contains Chars, which means that the folding function's second argument must be a Char. So here I'll assume that your insertRnd actually has the following signature:

insertRnd :: ∀ eff. String -> Char -> Eff (random :: RANDOM | eff) String

Third, you can't actually foldl over a String, because String doesn't have an instance of Foldable. To fold over the characters of a string, you need to first convert the string to an array (or some other container) of Char. Fortunately, there is a helpful function for that - toCharArray.

Fourth, your claim about not being able to compile "anything else that uses insertRnd" is probably too broad. Here's a perfectly compilable example of "anything else" that uses insertRnd:

main = launchAff do
    x <- liftEff $ insertRnd "abcd" 'x'
    log x

And finally, the reason that your foldl doesn't compile is that foldl expects a pure function a -> b -> a as first argument, but you're giving it an effectful function a -> b -> Eff _ a. No wonder there is a type mismatch!

The effectful analog of foldl is foldM. This function does a similar thing, except it chains the calls within a monad instead of doing pure functional composition. For example:

foldM insertRnd "a" ['b', 'c', 'd']

Applying all of the above to your code, here's the final version (assuming the existence of insertAt :: Int -> Char -> String -> String):

scramble :: ∀ eff. String -> Eff (random :: RANDOM | eff) String
scramble s = foldM insertRnd (take 1 s) (toCharArray $ drop 1 s)

insertRnd :: ∀ eff. String -> Char -> Eff (random :: RANDOM | eff) String
insertRnd st ch = do
  n <- randomInt 0 $ length st
  pure $ insertAt n ch st
Fyodor Soikin
  • 78,590
  • 9
  • 125
  • 172