Zulip Chat Archive

Stream: lean4

Topic: io fustration


Joseph O (Mar 08 2022 at 01:37):

how do people use io in lean?
lets say i read a file. the type is IO String, so i cant pass that into a function that takes string. how can I come across that? also getting a random item of a list is super annoying

Arthur Paulino (Mar 08 2022 at 01:42):

I think what you're experiencing is not exactly IO frustration, but monadic programming frustration.

If you have a : M α where M is some monad, say IO, and you're inside a do block, then you can do let a' : α ← a.

Joseph O (Mar 08 2022 at 01:43):

hmm. I can try

Arthur Paulino (Mar 08 2022 at 01:45):

Or if you have a function f : α → τ for some type τ, then you can do let t : τ := f (← a)

Joseph O (Mar 08 2022 at 01:49):

but in this function, i have to return an io monad

def getRandomSentence' (filename : FilePath) : Array String := do
  let fconts : String  FS.readFile filename
  fconts.split (·='\n')

Joseph O (Mar 08 2022 at 01:49):

this gives an error

Arthur Paulino (Mar 08 2022 at 01:53):

You need to be inside IO in order to call readFile:

def getRandomSentence' (filename : FilePath) : IO (List String) := do
  let fconts : String  FS.readFile filename
  pure $ fconts.split (·='\n')

Also, split returns a List, not an Array

Joseph O (Mar 08 2022 at 01:54):

Ok, but I dont want to return an IO (List String)

Arthur Paulino (Mar 08 2022 at 01:57):

What type do you want?

Joseph O (Mar 08 2022 at 01:57):

Just List String

Arthur Paulino (Mar 08 2022 at 01:59):

Yeah, I'm getting flashbacks of my own frustrations from two months ago

Mauricio Collares (Mar 08 2022 at 01:59):

https://www.cis.upenn.edu/~cis194/spring13/lectures/08-IO.html

Jakub Kądziołka (Mar 08 2022 at 01:59):

That is not possible, as such a value would break basically everything

Jakub Kądziołka (Mar 08 2022 at 02:00):

if a function needs to do IO, its return type will be wrapped in IO

Jakub Kądziołka (Mar 08 2022 at 02:00):

you can unwrap that with do-notation, but your other function will also have its type wrapped with IO, as it (transitively) needs to do IO

Joseph O (Mar 08 2022 at 02:31):

this is insane

Joseph O (Mar 08 2022 at 02:32):

well, sorta

Xubai Wang (Mar 08 2022 at 02:48):

Well, although it's a very bad practice, you can use unsafeIO.

It seems that you are using an external file to produce random sentences in Lean, so you can also read the file at compile time to ensure that it exists (see this for example).

Joseph O (Mar 08 2022 at 02:56):

I have no idea what you are doing there lol

Xubai Wang (Mar 08 2022 at 03:03):

It creates a new term include_str% which functions the same as include_str! macro in Rust (you seem to have a rust background?)

So for your case you can use:

def sentences := include_str% "<path to your file>"

def getRandomSentence : Array String := do
  sentences.splitOn '\n'

Arthur Paulino (Mar 08 2022 at 03:04):

Different monads can give you access to different computational resources. The IO monad gives you access to file reading and writing (and other cool things). But once you start a computation that relies on being in a certain monad, everything that depends on it will need to be in that monad too (or in a higher level monad, but don't worry about it for now)

Arthur Paulino (Mar 08 2022 at 03:07):

Defining the return type of a function as IO foo is how you say in Lean "this computation happens inside IO"

Arthur Paulino (Mar 08 2022 at 03:08):

And requiring such type is how Lean chains monadic computations

Joseph O (Mar 08 2022 at 03:19):

Xubai Wang said:

It creates a new term include_str% which functions the same as include_str! macro in Rust (you seem to have a rust background?)

So for your case you can use:

def sentences : String := include_str% "<path to your file>"

def getRandomSentence : Array String := do
  sentences.splitOn '\n'

I honestly don't know what include_str macro does, but it sounds like what I need

Joseph O (Mar 08 2022 at 03:19):

Ah yes, it is

Mario Carneiro (Mar 08 2022 at 03:31):

Xubai Wang said:

Well, although it's a very bad practice, you can use unsafeIO.

It's not just bad practice: keep in mind that if you use unsafeIO then the compiler will frequently miscompile your code, because it relies on the assumption that pure functions are pure

Jakub Kądziołka (Mar 08 2022 at 11:43):

Joseph O said:

this is insane

If you think that, you're probably misunderstanding how this approach works. It's really not that different from marking functions as pure or impure with a special keyword. Note that, outside of higher order functions, you'll never want to take IO _ as an argument.

Joseph O (Mar 08 2022 at 12:48):

What type of function is include_str% in lean?

Jakub Kądziołka (Mar 08 2022 at 12:50):

It isn't a function. It is a macro that gets expanded to the contents of a file at compile-time.

Xubai Wang (Mar 08 2022 at 12:53):

It expands into a string literal.

Joseph O (Mar 08 2022 at 12:54):

Oh that’s cool. Where can I find such other macros?

Xubai Wang (Mar 08 2022 at 13:08):

Unfortunately, there is no such a place, but actually it's almost everywhere, and many parts of Lean itself is implemented with it (namespace, section, etc...) Just search for builtinCommandElab in lean4 source code (and the whole Lean.Elab module).

If you want to find some other examples, this GH search may be useful https://github.com/search?q=language%3ALean+termelab&type=code.

There is also a youtube video on Lean4 metaprogramming. But it's not a comprehensive guide. You may look at some Lean 3 meta programming tutorials to get inspiration.

Joseph O (Mar 08 2022 at 13:12):

I would say I’m actually quite a bit familiar with the macros

Joseph O (Mar 08 2022 at 13:13):

But also, how did you find out about the macro?

Joseph O (Mar 08 2022 at 13:24):

also, @Xubai Wang image.png image.png

Jakub Kądziołka (Mar 08 2022 at 13:44):

looks like it's not built into Lean, check the link posted earlier

Henrik Böving (Mar 08 2022 at 17:41):

Joseph O said:

But also, how did you find out about the macro?

I wrote it for doc-gen4 and it was copied to xubai's project for his purposes, as explained in the github link

Patrick Stevens (Mar 09 2022 at 18:44):

The frustration you are experiencing is because Lean knows that "if f calls g, and g talks to the outside world, then f talks to the outside world". Many other languages do not know this, which makes their happy-paths simpler and easier to write, usually at the cost of having bizarre runtime errors when anything goes wrong. If the filesystem flakes out, for example, most languages will just throw an exception or something; Lean forces you to indicate that you know your function is vulnerable to filesystem flakiness, and propagate that knowledge to all your callers, by using the IO monad.

Yuri de Wit (Mar 09 2022 at 18:49):

And there are best practices on how to structure code with and without IO in that you want to try and keep IO in the "outer" shell of your application and the pure part inside it. Well, otherwise your whole app will be in IO. But I am no help here.

I think the whole assumption here is that the upside of this outweighs the difficulties.

Henrik Böving (Mar 09 2022 at 18:53):

For example doc-gen4 performs its analysis step inside the MetaM monad (which is quite a beefed up version of IO for meta programming) and does every little bit it of meta analysis there, then it spits out some result type and hands that over to the HTML generator which works in another much weaker monad (a Reader one) and simply presents the results of the analysis so the application has one part that can actually interact with the real world and another half that just presents the data


Last updated: Dec 20 2023 at 11:08 UTC