Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generate a random string at compile time or run time and use it in the rest of the program

What would be the best way to do this? unsafePerformIO? Template Haskell? Something else? I have never used either of those so I don't know many of the details of using them.

Note that the program will be compiled every time it is run, so it doesn't matter if I generate the string at compile time or run time. I also need to use this string in tons of places throughout the code so I can't really do it the 'proper' way and have it be an IO action, that would require far too much other code to be put into the IO monad.

like image 576
Drew Avatar asked Jul 06 '13 06:07

Drew


People also ask

How do you generate random strings?

Using the random index number, we have generated the random character from the string alphabet. We then used the StringBuilder class to append all the characters together. If we want to change the random string into lower case, we can use the toLowerCase() method of the String .

How do you generate a random string in CPP?

Example 1: Using the rand() Function to Generate Random Alphabets in C++ The following C++ program generates a random string alphabet by using rand() function and srand() function. The rand() function generates the random alphabets in a string and srand() function is used to seed the rand() function.

How do you generate a random text string in Java?

Using randomUUID() java. util. UUID is another Java class that can be used to generate a random string. It offers a static randomUUID() method that returns a random alphanumeric string of 32 characters.

How do you generate a random string in Swift?

In Swift, the randomElement() method is used to get a random character from a string.

Is it possible to generate random numbers at compile time?

Even so, I would like to add that it certainly is possible to generate random numbers at compile time, with the understanding that you'll get the same sequence of random numbers each time you run the program.

Is there a random number generator for C++ Template metaprograms?

There is a research paper on the topic: Random number generator for C++ template metaprograms containing code snippet for the __TIME__ trick. It also talks about supporting different random number engines and distributions as orthogonal choices. Show activity on this post.

What is the use of runtime initialization in C++?

Run time Array initialization Using runtime initialization user can get a chance of accepting or entering different values during different runs of program. It is also used for initializing large arrays or array with user specified values. An array can also be initialized at runtime using scanf () function.

Why compiler cannot resolve at compile time in C++?

Here the compiler cannot resolve at compile time because the value of the variable ‘var’ is a runtime activity. Hence the above string will be resolved at run-time.


2 Answers

I wouldn't recommend using unsafePerformIO. I guess The Haskell Report doesn't state that a constant function is memoized so it can happen that

randStringUnsafe :: String
randStringUnsafe = unsafePerformIO $ liftM (take 10 . randomRs ('a','z')) newStdGen

will give you different results for different calls! With GHC it's most likely going to be memoized, but without guarantees. For example, what if the compiler inlines the function? (GHC is probably smart enough not to do it, but again, no guarantees ...). And for example

randNumUnsafe :: (Random a, Num a) => [a]
randNumUnsafe = unsafePerformIO $ liftM (take 10 . randomRs (0, 9)) newStdGen

will definitely give you different results each time it's called.


I'd rather go with Template Haskell. It's perhaps a bit more complicated, but safe. In one module we define

{-# LANGUAGE TemplateHaskell #-}
module RandomTH where
import Control.Monad
import System.Random
import Language.Haskell.TH

-- A standard function generating random strings.
randString :: IO String
randString = liftM (take 10 . randomRs ('a','z')) newStdGen

-- .. lifted to Q
randStringQ :: Q String
randStringQ = runIO randString

-- .. lifted to an Q Exp
randStringExp :: Q Exp
randStringExp = randStringQ >>= litE . stringL

-- | Declares a constant `String` function with a given name
-- that returns a random string generated on compile time.
randStringD :: String -> DecsQ
randStringD fname = liftM (: []) $
    funD (mkName fname) [clause [] (normalB randStringExp) []]

(Perhaps randStringD could be written in a more readable way - if you have an idea, please edit it or comment.)

Then, in another module we can use it to declare a constant function with a given name:

{-# LANGUAGE TemplateHaskell #-}

$(randStringD "randStr")

main = do
    putStrLn randStr
    putStrLn randStr
like image 156
Petr Avatar answered Oct 08 '22 12:10

Petr


It might be easier to answer this question if we knew more about the surrounding context, but the approach I would take would be to pass in the string everywhere it was necessary, and create it once in main. Thus:

import Control.Monad
import System.Random

-- Some arbitrary functions

f :: String -> Int -> Int -> Int
f rstr x y = length rstr * x * y

-- This one doesn't depend on the random string
g :: Int -> Int
g x = x*x

h :: String -> String -> Int
h rstr str = sum . map fromEnum $ zipWith min rstr str

main :: IO ()
main = do
  rstr <- randomString
  putStr "The result is: "
  print $ f rstr (g 17) (h rstr "other string")

randomString :: IO String
randomString = flip replicateM (randomRIO (' ','~')) =<< randomRIO (1,32)

This is probably what I would do.

On the other hand, if you have a lot of these functions, you might potentially find it bulky to pass rstr into all of them. To abstract this, you can use the Reader monad; values of type Reader r a—or more generally, values of type MonadReader r m => m a—are able to ask for a value of type r, which is passed in once, at the top level. That would give you:

{-# LANGUAGE FlexibleContexts #-}

import Control.Applicative
import Control.Monad.Reader
import System.Random

f :: MonadReader String m => Int -> Int -> m Int
f x y = do
  rstr <- ask
  return $ length rstr * x * y

g :: Int -> Int
g x = x*x

h :: MonadReader String m => String -> m Int
h str = do
  rstr <- ask
  return . sum . map fromEnum $ zipWith min rstr str

main :: IO ()
main = do
  rstr <- randomString
  putStr "The result is: "
  print $ runReader (f (g 17) =<< h "other string") rstr

randomString :: IO String
randomString = flip replicateM (randomRIO (' ','~')) =<< randomRIO (1,32)

(Actually, since (r ->) is an instance of MonadReader r, the functions above can be viewed as having type f :: Int -> Int -> String -> Int, etc., and you can leave out the call to runReader (and remove FlexibleContexts)—the monadic computation you've built will just be of type String -> Int. But I probably wouldn't bother.)

Yet another approach, which is probably an unnecessary use of language extensions (I certainly prefer the two approaches above), would be to use an implicit parameter, which is a variable that gets passed around dynamically and reflected in the type (sort of like the MonadReader String m constraint). That would look like so:

{-# LANGUAGE ImplicitParams #-}

import Control.Monad
import System.Random

f :: (?rstr :: String) => Int -> Int -> Int
f x y = length ?rstr * x * y

g :: Int -> Int
g x = x*x

h :: (?rstr :: String) => String -> Int
h str = sum . map fromEnum $ zipWith min ?rstr str

main :: IO ()
main = do
  rstr <- randomString
  let ?rstr = rstr
  putStr "The result is: "
  print $ f (g 17) (h "other string")

randomString :: IO String
randomString = flip replicateM (randomRIO (' ','~')) =<< randomRIO (1,32)

Now. I must admit that you can do these sorts of things at the top level. There's a standard hack which allows using unsafePerformIO to get top-level IORefs, for instance; and Template Haskell would allow you to run an IO action once, at compile time, and embed the result. But I would avoid both of those approaches. Why? Well, fundamentally, there's some debate over whether "pure" means "determined exactly by the syntax/doesn't change over any run of the program" (an interpretation I would favor), or it means "doesn't change over this run of the program." As one example of the problems this caused: the Hashable package, at one point, switched from a fixed salt to a random salt. This caused an uproar on Reddit, and introduced bugs into previously-working code. The package backpedalled, and now allows users to opt-in to this behavior through an environment variable, defaulting to between-runs purity.

That said, here's how to use the two approaches that you mentioned, unsafePerformIO and Template Haskell, to get top-level random data—along with why, separate from the concerns about between-runs purity, I wouldn't use these techniques. (These are the only two techniques for doing this that I can think of.)

  1. The unsafePerformIO hack, as it's called, is very fragile; it relies on certain optimizations not being performed, and is generally not a well-liked approach. Doing it this way would look like so:

    import Control.Monad
    import System.Random
    import System.IO.Unsafe
    
    unsafeConstantRandomString :: String
    unsafeConstantRandomString = unsafePerformIO $
      flip replicateM (randomRIO (' ','~')) =<< randomRIO (1,32)
    {-# NOINLINE unsafeConstantRandomString #-}
    

    Seriously, though, see how much the word unsafe is used in the above code? That's because using unsafePerformIO will bite you unless you really know what you're doing, and possibly even then. Even when unsafePerformIO doesn't bite you directly, no less than the authors of GHC would say that it's probably not worth using for this (see the section titled "Crime Doesn't Pay"). Don't do this.

  2. Using Template Haskell for this is like using a nuclear warhead to kill a gnat. An ugly nuclear warhead, to boot. That approach would look like the following:

    {-# LANGUAGE TemplateHaskell #-}
    
    import Control.Monad
    import System.Random
    import Language.Haskell.TH
    
    thConstantRandomString :: String
    thConstantRandomString = $(fmap (LitE . StringL) . runIO $
      flip replicateM (randomRIO (' ','~')) =<< randomRIO (1,32))
    

    Note also that in the Template Haskell version, you can't abstract the random-string-creation functionality into a separate value randomString :: IO String in the same module, or you'll run afoul of the stage restriction. It is safe, though, unlike the unsafePerformIO hack; at least, safe modulo the concerns about between-run purity mentioned above.

like image 22
Antal Spector-Zabusky Avatar answered Oct 08 '22 14:10

Antal Spector-Zabusky