I'm trying to write a simple tower defense game in OCaml as a bit of a learning project and I'm having a problem with how I structure my code. I've been trying to avoid using classes/objects to represent my towers and enemies mostly because I haven't learnt how ocaml objects work, but mostly because I seem to have picked up an aversion to using them from my reading about OCaml. Anyway, what I've been trying to do is have a module for each type of tower, which all have the same signature (TOWER), implementing functions that the mainloop uses to update the game. My idea was to use first class modules so I could then have a function like
let create m =
let module M = (val m : TOWER) in
M.create ()
where TOWER is
module type TOWER = sig
type t
...
val create : unit -> t
end
But now I have a problem in that it seems that when you unwrap a first class module you get a brand new module, whereas I assumed you'd get something like an alias for the module, so at compile time I get an error saying there's a problem with the scope of the type constructor for M.t. From this and the tinkering I've tried to do it seems that my original idea of using first class modules to decide which function to use won't work unless I move the type declaration out of TOWER and make it a shared type that all towers use. Is there a way to get the effect I'm looking for or would I have to some other technique?
If you're using a first class module in a function, and there exists a type constructor in a function type, that depends on a type that is defined inside the module, then it means, that the type escape its scope, and you need to put your function into a prenex normal form, by bounding this type to a new type variable. To clarify this here is the example:
let create_tower (module T : TOWER) = T.create ()
This will not compile, since T.t. escapes the scope. Try to write a type for the function create_tower: (module TOWER) -> ?. We don't have a name for the type t, that is in the scope of the TOWER signature. So we need to bring it out, to get the following type:
(module TOWER with type t = 'a) -> 'a
To get this, we use the following syntax:
let create_tower (type t) (module T : TOWER with type t = t) =
T.create ()
Now it works.
And a usual rant about modules. There is no need to use modules or objects here. Just use records to represent your towers and enemies.
If you use a sum type to represent different towers, you don't have to bother with modules or objects.
For example :
type tower =
| SimpleTower
| SuperTower of int
type position = int * int
type pos_tower = position * tower
let string_of_tower = function
| SimpleTower -> "Simple tower"
| SuperTower i -> "Super Tower level " ^ (string_of_int i)
;;
let my_towers = [ ((0,0) , SimpleTower) ; ( (10,0) , SuperTower 5) ] ;;
The properties of such an architecture are dual to the properties of an object oriented architecture (functions are modular here whereas they crosscut in an object architecture).
Related
I was here for something else but trying to write up my question, I realize there's no way I'm doing this right.
I've been using mirage and irmin for a little while, and as long as all the code stays in the Main module everything is great. But of course, it quickly becomes a ridiculously huge file, and trying to split it in modules drives me mad with types escaping their scopes and whatnot.
Instead of just passing console from start to some other functions, I have to put those other functions in a functor that will take the Mirage_types_lwt.CONSOLE as well as the actual console variable, which means everything ends up being in functors instantiated from start and passed around in an unreadable mess of code.
I'm having problems making a giant ugly module to store and pass all of this easily (to "isolate" the parts that need this mess from regular code), and I can't figure out how to declare something like this :
module type ContextConfig = sig
module Store
val clientctx : Client.ctx
....
end
let mkContextConfig (module Store : Repo) ctx =
(module struct
(module Store : Repo)
let clientctx = ctx
end : ContextConfig)
(Repo being a module I made to wrap around Irmin's functors).
This obviously doesn't work, and I tried so many syntaxes I'm guessing it's just not possible, that means I'm doing something very wrong ?
I'd love advice on the proper way to deal with all of those functors and types in a clean way, how do I pass around things like the console or a conduit without having to functorize and instantiate everything in the Main module just to pass it around after ?
Definitions like that are possible:
module type REPO = sig end
module type CONTEXT_CONFIG = sig
module Store : REPO
val client_ctx : int
end
let mkContextConfig (module Store : REPO) ctx =
(module struct
module Store = Store
let client_ctx = ctx
end : CONTEXT_CONFIG)
(With some uninteresting changes to make the code type check.)
I am writing a lwt-based Twitter API library and I want to implement the Twitter API's cursor functionality using Lwt_stream library.
I decide to use Lwt_stream.from and supply this function an argument f.
Here is what I currently do
let get_follower ~access_token ~screen_name ?(count=5000) ?(wait=true) () =
let base_uri = "https://api.twitter.com/1.1/followers/ids.json" in
let cursor = ref (-1) in
let f () =
Client.do_get_request
~uri_parameters:
["screen_name", screen_name;
"cursor", (string_of_int (!cursor));
"count", (string_of_int count)]
~uri:(Uri.of_string base_uri)
~access_token:access_token
() >>= fun res ->
match res with
| Ok (header, str) -> begin
match (Yojson.Safe.from_string str |> user_ids_of_yojson) with
| `Ok uids ->
cursor := uids.next_cursor;
return (Some (Ok uids))
| `Error msg -> failwith msg
end
| Error e -> return (Some (Error (process_error_exn e))) in
Lwt_stream.from f
I am not sure if I should use ref. The reason I use ref is that the behavior of f depends on the value it returns previously. To be specific, the value of cursor which will be used next time depends on current next_cursor, and if cursor is zero, f knows it reaches the end and returns None.
Is using ref here considered a good design choice? Is there any better way to implement this functionality?
Antron gave an excellent answer to my opinion, but I would like just to share with few practical advice. If I were you, I wouldn't use one big function named f with a ref, well, mostly because it smells, and it is quite unreadable and doesn't scale. I would create a stream using Lwt_stream.create and work with it using a recursive function, that do the main loop, with all the logic separated in helper functions. The main loop function can have state with which it recurs, so that we don't need ugly references or explicit mutability.
So, below is an example, of how your code can be restructured. I also didn't find an explicit check for a zero cursor, that should be used to stop the stream (as you mentioned in text), so I added this. Otherwise the code should work as yours.
So, I've split it into three functions: make_request is responsible for making the request. parse_response is a pure transformation, that deconstructs the answer. And finally, loop function performs the main loop, makes requests, parses them and stops on zero.
Note: looks like that you're using Or_error monad and mixing it with exceptions. This is a bad idea, since it breaks precondition, that a well formed function that returns Or_error.t should never throw exception.
let get_follower ~access_token ~screen_name ?(count=5000) ?(wait=true) () =
let base_uri = "https://api.twitter.com/1.1/followers/ids.json" in
let make_request cursor =
Client.do_get_request
~uri:(Uri.of_string base_uri) ~access_token
~uri_parameters:[
"screen_name", screen_name;
"cursor", string_of_int cursor;
"count", string_of_int count
] () in
let parse_response = function
| Error e -> Error (process_error_exn e)
| Ok (header, str) ->
match Yojson.Safe.from_string str |> user_ids_of_yojson with
| `Ok uids -> Ok uids
| `Error msg -> failwith msg in
let stream,push = Lwt_stream.create () in
let rec loop cursor =
make_request cursor >|= parse_response >>= function
| Ok {next_cursor=0} -> push None; return_unit
| Ok {next_cursor=n} as r -> push (Some r); loop n
| error -> push (Some error); loop cursor in
async (fun () -> loop (-1));
stream
Update
But of course this implementation will eagerly pull data from the server, and push it downstream. It is like a pump and pipe system. As soon as function is invoked, the pump it turned on and it will constantly pour water into the system. You can put a tap, by using bounded push, that can be obtained with Lwt_stream.create_bounded. But you will still have some amount of prefetching (or back to our pipe system analogy, you will have some kind of extension tank). Usually, this is not bad, as it removes some latency, but sometimes this is not what you want. In this case the only choice, that is left is to use explicit reference cell, to control your loop (and build stream using Lwt_stream.from). Other approach would be to change the interface, maybe you're trying to pack to much stuff into one abstraction. Maybe returning follower list Lwt.t or even follower Lwt.t list would be better. Or you can even create an abstract type follower that would hide a thread and return follower list, and lift it's accessors into the _ Lwt.t, e.g.,
module Follower : sig
type t
val name : t -> string Lwt.t
...
end
In that case, this interface can be quite usable with Lwt_list module.
Since f takes unit and produces different results each time, as you've said, it will have to depend on some state, as I think you realize. It already does that by depending on the results of I/O, and I think this is what is making the question of the ref non-trivial to answer. Otherwise, the answer would be yes, it is necessary (see point (2) below).
I think there are two main possibilities for eliminating the syntactic ref, but neither of them makes sense.
Hide these state bits somehow inside the I/O f is already doing, i.e. on the peer across the API. This doesn't seem possible, in fact it looks like the bits have to be on the client for the API to work at all.
Hide these state bits somewhere else on the client. But there will still be something spiritually equivalent to a ref somewhere in the client, in one way or another. You could write or use some kind of wrapper that factors out this ref, but unless that wrapper is useful in many places, it would only risk making it less obvious that there is extra client-side state here. I would prefer to keep the state as local and as close to its usage as possible. Since f already necessarily does "dirty" things, f is the right place.
So, in summary, I would say keep the ref as it is. It is common for streams to be stateful anyway.
The above discussion assumes that you have to use Lwt_stream. If not, you can provide an alternative interface whose type looks something like get_follower : cursor:int -> ... -> (results * cursor) Lwt.t and let the caller worry about state if it has to. This would probably be what I'd try first.
EDIT
There is of course a disadvantage to that, which is the possibility of writing the calling code to supply the wrong cursor. You could hide the cursor by returning a partially applied function, but the calling code might be written to call the wrong function. Without linear types, the stream approach is safer if you are worried about these possibilities, so it has its advantages.
I am new to Haskell and wondered if there was a simple way of creating a data structure that I can add records to through successive function calls, and not lose the state of the structure in between function calls (persistent?), without the need for a database. So I have:
data Book = Book { title :: String
, author :: String
, ISBN :: String
}
deriving (Eq, Show)
type BookShelf = [Book]
I appreciate that Haskell's approach to doing this is to copy the data structure and return an update whenever one attempts to do something like this. So this would work something like:
addToBookShelf :: Book -> BookShelf -> BookShelf
addToBookShelf b bs = b : bs
So I could call this recursively from some other function to fill up BookShelf. I dont believe this will work for me as I am attempting to add records via a JSON PUT request, and I dont want to return the updated list for the function to be called recursively by an external HTTP client?? I'm sure there must be a simple answer to this!
Additional info: I'm using Web.Scotty to parse JSON POST/GET.
You will need to model the state somehow. Since you are new, let's go for a simple way: add a parameter to your server that keeps track of the state:
getBookViaJSON :: IO Book
-- listen, convert, ...
server :: BookShelf -> IO ()
server bookshelf = do
book <- getBookViaJSON
server (book:bookshelf)
This function never returns and never does anything with the bookshelf, so it's not useful in its own right, but you get the idea?
In an Isabelle theory file, I can write simple one-line tactics such as the following:
apply (clarsimp simp: split_def split: prod.splits)
I find, however, when I start writing ML code to automate proofs, to produce an ML tactic object, these one-liners become rather verbose:
clarsimp_tac (Context.proof_map (
Simplifier.map_ss (fold Splitter.add_split #{thms prod.splits})
#> Simplifier.map_ss (fn ss => ss addsimps [#{thm split_def}]))
#{context}) 1
Is there an easier way to write the simple one-line tactics at the Isabelle/ML level?
For example, something like an anti-quotation: #{tactic "clarsimp simp: split_def split: prod.splits"} producing a function of type context -> tactic, would be an ideal solution.
I see a variety of possibilities, it depends a bit on the context of your the application what is best. Note that in general, individual ML code for proof automated used to be common-place in the very old times, but it is relatively rare today. For example, compare the amount of custom tactics in rather small HOL-Bali (started in 1997) with the large JinjaThreads in AFP (started in 2007 and continued until recently).
Nesting ML antiquotations like #{tactic} would in principle work, but you would quickly run into further questions, like what happens if your theorem arguments should be again Isar or ML source.
Instead of antiquoting tactic building blocks in ML, a more basic approach is to quote your proof precedure in Isar by giving it regular method syntax like this:
ML {*
(*foo_tac -- the payload of what you want to do,
note the dependency on ctxt: Proof.context*)
fun foo_tac ctxt =
let
val my_ctxt =
ctxt |> Simplifier.map_simpset
(fold Splitter.add_split #{thms prod.splits} #>
Simplifier.add_simp #{thm split_def})
in ALLGOALS (clarsimp_tac my_ctxt) end
*}
method_setup foo = {*
(*concrete syntax like "clarsimp", "auto" etc.*)
Method.sections Clasimp.clasimp_modifiers >>
(*Isar method boilerplate*)
(fn _ => fn ctxt => SIMPLE_METHOD (CHANGED (foo_tac ctxt)))
*}
Here I've first made a conventional foo_tac definition in Isabelle/ML, and then wrapped it up the usual Isar way as proof method. The latter means you have wrappers like SIMPLE_METHOD taking care of pushing "chained facts" into your goal state, and CHANGED to ensure that the Isar method makes progress (like simp or auto).
The foo_tac example assumes that your modification of the context (or its simpset) is constant, by the hard-wired split rules. If you want to have further parameters there, you can include that in the concrete method syntax. Note that Method.sections is already quite sophisticated in this respect. More basic argument parsers are given in the section "Defining proof methods" of the isar-ref manual. You should also look at existing examples by searching the sources for method_setup (in Isabelle/Isar) or Method.setup (in Isabelle/ML).
If you still want to do ML antiquotations instead of concrete method syntax, one could try a variant of #{context} that allows modifiers like this:
#{context simp add: ...}
That is a bit speculative, invented on the spot, and might turn out as bad practice. As I've said, fine-grained tactic programming in Isabelle became a bit out of use in recent years, although ML is an integral part of the Isabelle framework. If you pose a more concrete question with more of the application context, we can reconsider the antiquotation approach.
Additional to the other answers, I think it's worth mentioning that there is a new high-level tactic/proof method construction language (similar to Ltac in Coq) called Eisbach in Isabelle2015, which aims to be easier to understand and maintain.
The Method class appear to provide enough of an interface to extract out a tactic, via a cases_tactic as follows:
(*
* Generate an ML tactic object of the given Isar string.
*
* For example,
*
* mk_tac "auto simp: field_simps intro!: ext" #{context}
*
* will generate the corresponding "tactic" object.
*)
fun mk_tac str ctxt =
let
val parsed_str = Outer_Syntax.scan Position.start str
|> filter Token.is_proper
|> Args.name
val meth = Method.method (Proof_Context.theory_of ctxt)
(Args.src (parsed_str, Position.start)) ctxt
in
Method.apply (K meth) ctxt [] #> Seq.map snd
end
or alternatively as an anti-quotation:
(*
* Setup an antiquotation of the form:
*
* #{tactic "auto simp: foo intro!: bar"}
*
* which returns an object of type "context -> tactic".
*
* While this doesn't provide any benefits over a direct call to "mk_tac" just
* yet, in the future it may generate code to avoid parsing the tactic at
* run-time.
*)
val tactic_antiquotation_setup =
let
val parse_string =
((Args.context -- Scan.lift Args.name) >> snd)
#>> ML_Syntax.print_string
#>> (fn s => "mk_tac " ^ s)
#>> ML_Syntax.atomic
in
ML_Antiquote.inline #{binding "tactic"} parse_string
end
and setup in a theory file as follows:
setup {*
tactic_antiquotation_setup
*}
which can then be used as follows:
lemma "(a :: nat) * (b + 1) = (a * b) + a"
by (tactic {* #{tactic "metis Suc_eq_plus1 mult_Suc_right nat_add_commute"} #{context} *})
as desired.
I have a number of events that happen in a game. I want to control the time and order at which these events occur.
For example:
Event 1: Show some text on screen for N frames & play a sound effect
Event 2: Clear the text on the screen
My solution (maybe there is a better one), is to have a list of functions that contain the events. The events perform their behavior then return the next events to occur in the game. I thought of using List.map or List.collect because I am essentially mapping a list of events to a new list of events while performing some behavior code.
In the example above, Event1 can be composed of two functions: One that shows text and one that plays a sound (hence the need for a list). The function that shows text would return a copy of itself for N-1 frames then it would return Event2 which clears the text. The play sound function would return the equivalent of a no-op.
If this is a good solution, I could probably do it in C++ or C#. My goal is to do an equivalent or better solution in F#.
Did you mean something like this?
let myActions =
[fun () -> printfn "You've woken up a dragon."
fun () -> printfn "You hit the dragon for 0 points of damage."
fun () -> printfn "The dragon belches."
fun () -> printfn "You have died."]
let actionBuilder actionList =
let actions = ref actionList
fun () ->
match !actions with
| [] -> ()
| h::t -> h(); actions := t
Usage (F# interactive):
> let doSomething = actionBuilder myActions;;
val doSomething : (unit -> unit)
> doSomething();;
You've woken up a dragon.
val it : unit = ()
> doSomething();;
You hit the dragon for 0 points of damage.
val it : unit = ()
> doSomething();;
The dragon belches.
val it : unit = ()
> doSomething();;
You have died.
val it : unit = ()
> doSomething();;
val it : unit = ()
>
**Edit: ** if you want to be able to add actions, maybe it's better to make an action dispenser that uses a Queue internally, since appending is O(N) with a list and O(1) with a Queue:
type actionGenerator(myActions: (unit->unit) list) =
let Q = new System.Collections.Generic.Queue<_>(Seq.ofList myActions)
member g.NextAction =
fun () ->
if Q.Count = 0 then ()
else Q.Dequeue()()
member g.AddAction(action) = Q.Enqueue(action)
Not quite sure what you're trying to achieve here... it can be helpful to think through the exact types that you're looking for. It sounds like perhaps you want to map a (unit->(unit->unit)) list to a (unit->unit) list by applying each function in the first. If that's the case, you can do it like so:
let l = [(fun () -> (fun () -> printfn "first nested fn")); (fun () -> (fun () -> printfn "second nested fn"))]
let l' = List.map (fun f -> f()) l
If you are looking for a syntax to declare your list type then here is one way to do this:
List<`a->`b>
This assumes that the function takes a single parameter.
But the very fact that you are trying to figure out the syntax for the type is a hint that you are still looking at this as if you are coding in procedural language.
The "functional" way of doing it is to concentrate on the logic of generating the list and let the compiler to infer the type based on your code
I've read your question twice, and am still not sure I understand exactly what you want. But from what I understand, your 'events' aren't necessarily invoked in the order they appear in the 'list'. If this is the case, you don't really want an F# list, you want some kind of lookup.
Now the other question is whether it is really a good idea for an event to determine what should follow it? That kind of amounts to hard coding your functionality once and for all, doesn't it?
EDIT
I see in a comment that you say you want to 'chain function calls together'.
How about writing them one after the other? We aren't in Haskell after all, F# will fire them in the order you write them.
If you want to be a little bit more functional, you could use continuations - each function takes an extra parameter which is the next function to execute. Almost monadic (I believe), except that in your case they appear to be actions, so there are no values to string through from one function to the next.
Not sure if that helps: I think you'll have to try rephrasing your question, judging by the diversity of answers here.
It seems like you are trying to do something in a very complicated way. That is sometimes necessary, but usually it isn't.
Since you are asking this question, I assume that you have more experience in imperative languages. It seems that the real solution to your problem is something completely different than a list of functions.