Jared Wheeler COP6557 Languages Research Paper Programming languages are diverse in variety and implementation. Each has unique points and some commonalities. Polog is a programming language which has survived the test of time, yet has never become mainstream due to its specificity to its problem solving area. Clojure is a modern adaptation of Lisp-1 to run on JVM and other more object-oriented virtual machines. F# is Microsoft’s foray into functional programming for their .NET framework. Each language has interesting implementation to solve certain problems. Prolog, “Programming in Logic”, is a declarative language which solves problems for the programmer. Developed in 1972 by Alain Colmerauer and Phillipe Roussel, Prolog was one of the first successful logic programming languages. Prolog is based upon facts, rules, and queries. Facts are basic assertions, rules are inferences relating facts, and queries are questions; the first two are stored in a knowledge base file and the queries are input by the user. Facts are identified as a predicate with its two targets in parenthesis; this grows into a graph-like structure of relationships between targets. Rules and queries determine datatypes by naming constraints: lowercase words are constants, those that start with uppercase or underscore are variables that Prolog will try to determine at runtime. Lists are indicated by square brackets ([]) and are of variable length, while tuples indicated by parenthesis have variable length. Lists and Tuples can be divided with pipe (|) delimiters, to where the first element is on the left, the rest on the right, frequently referred to as [Head|Tail]. Recursion is possible within the Rules; however it can very easily cause a memory overflow. The use of Tail Recursion Optimization, placing the recursive call at the end of the Rule, will cause Prolog to discard the previous stack when it enters the nesting, and therefore avoids the overflow. Unlike other languages, Prolog has no assignments; rather it unifies the variable with possible constants seeking two that match. Unification is the key to how Prolog solves queries; it performs depth-first traversal of its constructed graph of rules, utilizing the inferences to guide it towards the answer for the query. In the end, it will relay the successful unifications to the user as an answer to each variable in the query. All of this relays Prolog as a simply written, yet powerful, language for problem solving. Perhaps the most confusing part of the language is the output, which will be the discovered answers, followed by a yes or no. This yes or no answer is to the unspoken question of if execution completed. The program responds yes if execution of its search across the knowledgebase completed. It answers no if the execution could not be completed, and therefore the answers provided may be complete or incomplete. On top of this lack of commitment to complete answers, Prolog has a steep learning curve, especially for developers. Developers are used to designing an algorithm to solve a problem; Prolog is the algorithm for solving a problem, it simply requires a complete mapping of the problem space and a query that properly relays the desired question across the knowledge base. Prolog is implemented to run in many environments, from GNU to JVM or CLR, although these different implementations may have additional features or quirks that general Prolog does not have. Prolog is proven to be powerful for solving problems in systems with constraints, such as puzzles, AI, and natural language processing; however it has problems scaling to very large data sets, has a steep learning curve, and cannot be used outside of its specialty area. Clojure is the language that wants to bring Lisp into modern times. It merges the ideals of Lisp with the power of modern OOP, powered by JVM, but also adaptable for CLR, JavaScript, and Python. Clojure is a dynamically typed functional language with the methodology of “data as code”. Clojure, like Lisp, utilizes a prefix notation and a large number of parentheses, for all operations within the language. Clojure utilizes the underlying JVM datatypes, and then has four primary data structures used in it. These four data types are List, Vector, Set, and Map. A list is an ordered collection of elements surrounded by parenthesis; lists are used idiomatically to hold code as they are always evaluated as functions. Vectors are an ordered collection of elements optimized for random access, surrounded by square brackets; these are used idiomatically to hold data. Maps are key-value pair listings surrounded by braces; Clojure allows commas to delimit the pairs. Also, maps can utilize the keyword as a function to find the value. Sets are indicated by a pound, and then surrounded by braces; they contain unordered elements. Sets can be used as a function to determine if a searched for element is within it. Functions are defined by the defn function with a name and a vector of parameters. Clojure allows interfaces, created by defprotocol and instantiated with defrecord. The datatypes in Clojure are immutable, and to change values, will be recreated. Clojure allows the use of let to temporarily bind a value to a variable. Clojure also allows the use of anonymous functions through use of pound parenthesis around the body of the function. All collections, strings, file systems, and most Java containers used in Clojure implement Sequences, which give a wide variety of preexisting functions, such as every?, some?, filter, and for to make for loop-like structures. Also, Clojure features lazy evaluation of ranges so that it is possible to do actions on infinite sets. Finally, as a functional language, Clojure gives better concurrency safeguards, specifically the utilization of Software Transactional Memory (STM). STM makes it so that references, data defined by a ref to data, can only be changed within a transaction scope. The transaction scope operates like that of a database to stop conflicting data manipulation. Clojure also allows atoms to be built: blocks of data which cannot be modified, but can be overwritten. However, Clojure is a complex language. Macros were extremely dangerous in Lisp, and while some macros were not implemented within Clojure, they are still a danger for inexperienced users. Macros allow users to utilize the “data as code” philosophy to create their own keywords. The prefix notation takes effort in a programming world dominated by infix notation. Also, Clojure, despite being a functional language, lacks tail recursion optimization due to the JVM unable to directly support it. Therefore, in order to optimize recursive calls, the developer must put a loop at the start, and then the keyword recur with the parameters being passed to indicate that recursion is being used. This method of recursion is far less readable than other functional languages, especially with the lack of readability that Clojure already presents with the sheer number of parenthesis. Clojure is flexible and powerful; however this comes with costs of readability and complexity. F# is functional language built by Microsoft for its CLR. F# is similar to Clojure in its language goals of flexibility and power; however it does not abide by the same “data as code” mentality. F# actually has fewer parentheses than standard programming languages. Functions are still called in prefix notation; however, rather than the parenthesis of Clojure and Lisp, F# is white-space sensitive. Variables are bound to values in let statements; once bound the value is immutable. It is possible to add an attribute, “modifiable”, to the binding statement, therefore allowing a new value to be designated for that variable. F# is a statically typed language; however adopts the look of a dynamic through type inferencing, allowing the same powerful type-checking of the rest of the .NET Framework. It is possible to specify the variable type through a (variableName:type) syntax; however this should only be necessary during some function calls. Functions are a datatype in F#, and can therefore be passed to other functions. Anonymous functions are built by lambda expressions, where a signature indicates its function body with “->”. The three primary data structures are lists, arrays, and records. Lists are denoted by square brackets containing a semi-colon delimited list; or they can be created as a range with a “..” separating the start and exclusive stop numbers. List.map and List.filter allow functions to be run across lists, with the list to be affected named after the function and its argument. Arrays are enclosed with square bracket and pipe ([| |]). Records are grouped data with each member having a name; these are defined as types in the language and are enclosed with brackets. Records are also immutable; however can be rebuilt with altered values through {name with member=newValue} syntax. Inference when instantiating a custom type can be sometimes wrong; however by invoking the member from the parent type, it tells F# what type the entire object is. Attributes can also be optional, specified as having “None” or “Some “+value. Discriminating joins allow for enumerations to be used. F# also features the forward pipe operator (|>), which cuts down on function nesting by giving the functions to be called in the order they are to be called. The power, and complexities, of F# comes from the following traits of the language. Currying is that despite multiple parameters in a function, each function is treated as having one parameter which returns a function with another parameter, and so on until all of the parameters are used. This allows more modular work when modifying the language through use of newly defined operators. However, defining new operators has a danger of either overloading previous operators or completely overwriting them. Overloads of operators can be specified within the local scope of a type. Active patterns and Quotations build powerful sub-language abilities. Active patterns allow custom pattern matching within F#. Quotations, designated by “<@ … @>”, can hold code fragments which will be type checked, but not executed. Together they allow F# to be a powerful language for cross-compilation. F# 3.0 allows for execution of the quotations by the CLR if desired. F# has very powerful and complex abilities, allowing for great power to the user if they learn the idiosyncrasies of the language. Prolog is the oldest of these languages, and has been adapted to many different runtime environments. Clojure is the adaptation of Lisp for the JVM, and F# is a language for the Microsoft CLR. Prolog was built to solve constrained problems, which it does with relative simple syntax, yet a complex mindset. Clojure sought to make Lisp more modern and fix some of its shortcomings. F# is a functional language of experimentation for Microsoft. All three succeed at their goals, although their price is complexity and the learning curve.