Module Prelude.PreludeIO

This package provides classes and functions that deal with the lesser pure parts of Frege - Exceptions, Threads, mutable data.

This package is implementation specific insofar as the compiler may assume that certain items are defined here in a certain way. Changes may thus lead to compiler crashes or java code that will be rejected by the java compiler.

In particular, derived Exceptional instances will reference type class Exceptional.

Imports

Table of Content

Definitions

class JavaType e

Make the java.lang.Class object corresponding to the instantiated type available.

An instance for this type class is needed for the element type of JArrays (which are based on reflective true Java arrays) one needs to create.

A subclass of this is Exceptional which adds no additional functionality and behaves basically just as a marker interface for exceptions.

JavaType instances are derivable for all data types.

Known Instances

NoMatch, GuardFailed, Exception, Undefined

Member Functions

javaClassJavaType e ⇒ Class e

The java.lang.Class object of the instantiated type

class JavaType e ⇒ Exceptional e

Function catch requires that the argument of a handler function is an instance of Exceptional.

This is derivable for pure native data types.

Known Instances

NoMatch, GuardFailed, Exception, Undefined

data Exception = pure native java.lang.Exception
doCatchClass bST s a → (b → ST s a) → ST s a
native PreludeBase.WrappedCheckedException.doCatch

nowarn: argument of type (ST s a)

Runtime method for implementation of catch

catchExceptional γ ⇒ ST β α → (γ → ST β α) → ST β α
infixl  2

The construct

 action `catch` handler

is a ST action with the same type as action.

If action yields a result, this will be the result of the overall action. However, if, during execution of action the JVM raises an exception e with java type E, and E is a subtype of java type H, and H is the java type associated with the argument of handler, the return value will be:

 handler e

Otherwise, if the type of e does not allow to pass it to handler it will be propagated upwards and a `catch` b will not return to its caller.

Because catch is left associative, it is possible to catch different exceptions, like in:

 action `catch` handler1 `catch` handler2

Care must be taken to check for the most specific exception first. In the example above, if the exception handled by handler1 is less specific than the one handled by handler2, then handler2 will never get a chance to execute.

Another way to put this is to say that if E1 and E2 are distinct exception types handled in a chain of catches, and E1 is (from the point of view of Java!) a subtype of E2, then the handler for E1 must appear further left than the handler for E2. If it is a super type of E2, however, its handler must appear further right. And finally, if the types do not stand in a sub-type relationship, the order of the handlers is immaterial.

Note If action is of the form:

 doSomething arg

then, depending on the strictness of doSomething the argument arg may be evaluated before the action is returned. Exceptions (i.e. undefined values) that occur in the construction of the action do not count as exceptions thrown during execution of it, and hence cannot be catched.

Example:

 println (head []) `catch`  ....

will not catch the exception that will be thrown when println evaluates

For a remedy, see try.

finallyIO aIO bIO a
native PreludeBase.WrappedCheckedException.doFinally
infixl  2

nowarn: argument of type (ST s a)

The construct

 action `finally` always

returns the same value as action, when executed.

However, no matter if action produces a value or diverges (for example, by throwing an exception), in any case will always be executed, and its return value dismissed.

Note that finally only returns to its caller if action would have done so.

finally has the same fixity as catch, hence it is possible to have

 action `catch` handler1 `catch` handler2 `finally` always
throwSTThrowableST s a

Deliberately throw an exception in the ST monad.

throwIOThrowableIO a

Deliberately throw an exception in the IO monad.

try ∷ (Applicative 𝖆, Bind 𝖆) ⇒ (𝖈→𝖆 𝖇) → 𝖈 → 𝖆 𝖇

Make sure that exceptions thrown during construction of an action can be catched. See catch for an explanation.

Example:

 try println (head []) `catch` (\u::Undefined -> println u.catched)

should print:

 frege.runtime.Undefined: Prelude.head []

try does work for unary functions only. To be safe with functions taking more actions, use:

 pure a >>= (\a -> pure b >>= (\b -> f a b))
catchAllST α βST α (Exception | β)

The catchAll function runs a ST action and returns either the result or the exception thrown.

data Mutable s m

Mutable is a wrapper for native data types. A value of type Mutable s x is really an x, but it is tied to the ST thread s, from which it cannot escape.

The compiler will enforce the following rules:

  1. When type checking x.m and (x::Mutable s T), m will be searched in the namespace T
  2. An impure native method must only take and return Mutable s T, unless T is itself pure (of course, all algebraic data types are pure)
  3. A pure type T must not appear as Mutable s T in native functions.
  4. Pure native functions may not return Mutable s t for any t.
  5. Pure native functions must not normally get arguments of type Mutable s T. (see below)
  6. In a type of the form ST x (Mutable y d), it will be enforced that x is the same type as y. Furthermore, all phantom types of all Mutable types must be the same in a type signature.

To understand the motivation for rule 5, observe that the Mutable.Mutable data constructor cannot be applied in Frege code, hence the only possibility to obtain a value of type Mutable s t is through a native function. But by rule 4 above, this could only happen in the ST or IO monad, from whence those values cannot escape. The ST monad is a context where the sequence of actions matters. If we allowed passing mutable data to pure functions, their results would depend on whether ST actions modified the value before the result is actually evaluated.

Although in a strict sense, no pure function should get mutable data, rule 5 is only enforced for native pure functions, as normal frege functions couldn't do anything with the native value, except passing them on to native functions eventually.

There will be means to get a read-only copy (Freezable.freeze) of a mutable value through class Freezable.

There is also the possibility that, although a value is mutable, there are certain properties that cannot change (such as the length of an array). It will be possible to bypass rule 5 in such cases.

To summarize:

Implementation note: It is assumed that Mutable is a newtype.

Constructors

private Mutable m

Member Functions

freezeFreezable m ⇒ Mutable s mST s m

obtain a read-only copy of a mutable value through cloning or serialization.

readonly ∷ (m → b) → Mutable s mST s b

Apply a pure function to a mutable value that pretends to be read-only.

The function must not rely on anything that could change in the mutable data!

thawFreezable m ⇒ m → STMutable s m

obtain a mutable copy of a read only value through cloning or serialization.

readonly ∷ (m → b) → Mutable s mST s b

Alias for Mutable.readonly

mutable ∷ 𝖇 → Mutable 𝖆 𝖇

Obtain a read only native value (temporarily) typed as Mutable

For example, this could be needed when running a native ST method that uses the value as read-only, but we can't declare it, like:

 private native copyOf java.util.Arrays.copyOf
               :: ArrayOf s HashMap -> Int -> ST s (ArrayOf s HashMap)
type MutableIO = Mutable RealWorld

They type for mostly mutable values that are tied to the IO monad.

For java types that are mutable only so that they always would occur wrapped into MutableIO, the convention is to declare them as

 data Thing = mutable native org.mut.impure.Thing

and just write Thing everywhere. The type checker will check the rules for native functions as if Thing was MutableIO Thing.

However, normal type unification does not take the mutable status into account, so Mutable a m will never unify with Thing.

type IOMutable d = ST RealWorld (Mutable RealWorld d)

They type of IO actions that return a mutable value of type d

This is an abbreviation for ST RealWorld (Mutable RealWorld d)

type STMutable s d = ST s (Mutable s d)

They type of ST actions that return a mutable value of type d

This is an abbreviation for ST s (Mutable s d)

class Freezable f

Type class for mutable values that support making read-only copies. To be implemented with care.

Member Functions

freezeFreezable f ⇒ f → f

"Freeze" a mutable native value. The result is supposed to be immutable or at least not reachable from other parts of the code, especially from java code.

The most prominent way to freeze a value is by Cloneable.clone-ing it, if that is supported. But note that sometimes a deep copy would be needed, and that clone does not do that.

thawFreezable f ⇒ f → f

The inverse of Freezable.freeze creates a value (an object) which can be passed to impure functions without compromising the frozen object passed as argument.

One possibility to thaw an object is by cloning it.

If Freezable.thaw is not implemented correctly, bad things may happen.

class Freezable f ⇒ Cloneable f

For a data type declared like

 data D = native Javatype

where Javatype implements the java.lang.Cloneable interface, one can get implementations for Freezable.freeze and Freezable.thaw by just stating

 instance Cloneable D

The Freezable.freeze and Freezable.thaw operations are implemented in terms of Cloneable.clone.

Note: Cloning does not produce safe copies if the cloned object contains references to mutable objects. In such cases, sort of a deep cloning would be required.

Member Functions

cloneCloneable f ⇒ f → f
pure native clone

clone v must be a native method that works like java.lang.Object#clone.

freezeCloneable f ⇒ f → f
thawCloneable f ⇒ f → f
class Freezable f ⇒ Serializable f

For a data type declared like

 data D = native Javatype

where Javatype implements the java.io.Serializable interface, one can get implementations for Freezable.freeze and Freezable.thaw by just stating

 instance Serializable D

The Freezable.freeze and Freezable.thaw operations are implemented in terms of Serializable.copySerializable, which serializes its argument to a byte array and creates a new copy by deserializing it from the byte array.

Member Functions

copySerializableSerializable f ⇒ f → f
pure native frege.runtime.Runtime.copySerializable

copySerializable v is supposed to be a native function that is implemented by frege.runtime.Runtime.copySerializable at the instantiated type.

freezeSerializable f ⇒ f → f

make a safe copy through serialization/deserialization

thawSerializable f ⇒ f → f

make a safe copy through serialization/deserialization

data Ref a = native frege.runtime.Ref

A mutable reference, suitable for use in the ST monad.

Member Functions

getMutable s (Ref a)ST s a
native get

get the value the reference is pointing to

modifyMutable 𝖇 (Ref 𝖆) → (𝖆→𝖆) → ST 𝖇 ()

modify the referenced value with a function

newaSTMutable s (Ref a)
native new

create a reference that is initially set to the argument value

putMutable s (Ref a)aST s ()
native put

assign another value to the reference

type IORef a = Mutable RealWorld (Ref a)

Haskell compatibility

readIORefMutable s (Ref a)ST s a
native get

Alias for Ref.get

writeIORefMutable s (Ref a)aST s ()
native put

Alias for Ref.put

modifyIORefMutable 𝖇 (Ref 𝖆) → (𝖆→𝖆) → ST 𝖇 ()

Alias for Ref.modify

newIORefaSTMutable s (Ref a)
native new

Alias for Ref.new

Instances

instance Exceptional Exception

Member Functions

javaClassClass Exception
pure native java.lang.Exception.class
instance Exceptional GuardFailed

Member Functions

javaClassClass GuardFailed
pure native frege.runtime.GuardFailed.class
instance Exceptional NoMatch

Member Functions

javaClassClass NoMatch
pure native frege.runtime.NoMatch.class
instance Exceptional Undefined

Member Functions

javaClassClass Undefined
pure native frege.runtime.Undefined.class

Functions and Values by Type

Class GuardFailed

Exceptional_GuardFailed.javaClass

Class NoMatch

Exceptional_NoMatch.javaClass

Class Undefined

Exceptional_Undefined.javaClass

Class Exception

Exceptional_Exception.javaClass

ThrowableIO a

throwIO

Cloneable f ⇒ f → f

Cloneable.thaw, Cloneable.clone, Cloneable.freeze

Freezable f ⇒ f → f

Freezable.freeze, Freezable.thaw

Serializable f ⇒ f → f

Serializable.thaw, Serializable.copySerializable, Serializable.freeze

JavaType e ⇒ Class e

JavaType.javaClass

IO a → IO b → IO a

finally

ST α β → ST α (Exception | β)

catchAll

Mutable s (Ref a) → a → ST s ()

Ref.put

Mutable s (Ref a) → ST s a

Ref.get

Mutable 𝖇 (Ref 𝖆) → (𝖆→𝖆) → ST 𝖇 ()

Ref.modify

ThrowableST s a

throwST

a → STMutable s (Ref a)

Ref.new

m → Mutable s m

Mutable.Mutable

𝖇 → Mutable 𝖆 𝖇

mutable

Freezable m ⇒ Mutable s m → ST s m

Mutable.freeze

Freezable m ⇒ m → STMutable s m

Mutable.thaw

(m → b) → Mutable s m → ST s b

Mutable.readonly

Class b → ST s a → (b → ST s a) → ST s a

doCatch

Exceptional γ ⇒ ST β α → (γ → ST β α) → ST β α

catch

(Applicative 𝖆, Bind 𝖆) ⇒ (𝖈→𝖆 𝖇) → 𝖈 → 𝖆 𝖇

try

Valid HTML 4.01 Strict