From geoff@REDACTED Sun Oct 1 01:44:27 2000 From: geoff@REDACTED (Geoff Wong) Date: Sun, 1 Oct 2000 10:44:27 +1100 (EST) Subject: R7B Open Source Erlang available for download In-Reply-To: from "Kent Boortz" at Sep 29, 2000 02:35:44 PM Message-ID: <200009302344.XAA17621@gecko.serc.rmit.edu.au> Here's a set of patches to R7B-0 that enables it compile the ssl and crypto library modules with OpenSSL (installed separately). Geoff --- erts/autoconf/configure.in Sun Oct 1 10:25:23 2000 +++ erts/autoconf/configure.in.ORIG Sun Oct 1 10:17:38 2000 @@ -580,33 +580,16 @@ AC_SUBST(ded_soname) dnl -dnl SSL and CRYPTO needs the library openSSL/ssleay +dnl SSL and CRYPTO needs the library ssleay dnl +AC_SUBST(SSLEAY_ROOT) SSLEAY_ROOT= -SSL_INCLUDE= if test -d ${ERL_TOP}/lib/ssl/usr/ssleay; then - SSLEAY_ROOT='$(ERL_TOP)/ssl/usr/ssleay/$(TARGET)' - SSL_INCLUDE='-I$(ERL_TOP)/lib/ssl/usr/ssleay/include' - AC_SUBST(SSL_INCLUDE) - AC_SUBST(SSLEAY_ROOT) + SSLEAY_ROOT='$(ERL_TOP)/lib/ssl/usr/ssleay/$(TARGET)' else - echo "I could not find OpenSSL or SSLeay" > ${ERL_TOP}/lib/ssl/SKIP - echo "I could not find OpenSSL or SSLeay" > ${ERL_TOP}/lib/crypto/SKIP - for dir in /usr /usr/pkg /usr/local /usr/local/ssl /usr/lib/ssl /usr/ssl; do - AC_CHECK_HEADER($dir/include/openssl/opensslv.h, ac_cv_openssl=yes, ac_cv_openssl=no) - if test $ac_cv_openssl = yes ; then - SSLEAY_ROOT="$dir" - SSL_INCLUDE="-I$dir/include/openssl -I$dir/include" - /bin/rm ${ERL_TOP}/lib/ssl/SKIP - /bin/rm ${ERL_TOP}/lib/crypto/SKIP - AC_SUBST(SSL_INCLUDE) - AC_SUBST(SSLEAY_ROOT) - fi - done + echo "I could not find ssleay" > ${ERL_TOP}/lib/ssl/SKIP + echo "I could not find ssleay" > ${ERL_TOP}/lib/crypto/SKIP fi - -AC_SUBST(SSL_INCLUDE) -AC_SUBST(SSLEAY_ROOT) #-------------------------------------------------------------------- --- erts/autoconf/configure Sun Oct 1 10:25:45 2000 +++ erts/autoconf/configure.ORIG Sun Oct 1 10:28:58 2000 @@ -1366,7 +1366,7 @@ done test -n "$YACC" || YACC="yacc" -for ac_prog in gawk mawk nawk awk +for ac_prog in mawk gawk nawk awk do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 @@ -4417,72 +4417,22 @@ + SSLEAY_ROOT= -SSL_INCLUDE= if test -d ${ERL_TOP}/lib/ssl/usr/ssleay; then - SSLEAY_ROOT='$(ERL_TOP)/ssl/usr/ssleay/$(TARGET)' - SSL_INCLUDE='-I$(ERL_TOP)/lib/ssl/usr/ssleay/include' - - -else - echo "I could not find OpenSSL or SSLeay" > ${ERL_TOP}/lib/ssl/SKIP - echo "I could not find OpenSSL or SSLeay" > ${ERL_TOP}/lib/crypto/SKIP - for dir in /usr /usr/pkg /usr/local /usr/local/ssl /usr/lib/ssl /usr/ssl; do - ac_safe=`echo "$dir/include/openssl/opensslv.h" | sed 'y%./+-%__p_%'` -echo $ac_n "checking for $dir/include/openssl/opensslv.h""... $ac_c" 1>&6 -echo "configure:4434: checking for $dir/include/openssl/opensslv.h" >&5 -if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then - echo $ac_n "(cached) $ac_c" 1>&6 -else - cat > conftest.$ac_ext < -EOF -ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" -{ (eval echo configure:4444: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } -ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` -if test -z "$ac_err"; then - rm -rf conftest* - eval "ac_cv_header_$ac_safe=yes" -else - echo "$ac_err" >&5 - echo "configure: failed program was:" >&5 - cat conftest.$ac_ext >&5 - rm -rf conftest* - eval "ac_cv_header_$ac_safe=no" -fi -rm -f conftest* -fi -if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then - echo "$ac_t""yes" 1>&6 - ac_cv_openssl=yes + SSLEAY_ROOT='$(ERL_TOP)/lib/ssl/usr/ssleay/$(TARGET)' else - echo "$ac_t""no" 1>&6 -ac_cv_openssl=no -fi - - if test $ac_cv_openssl = yes ; then - SSLEAY_ROOT="$dir" - SSL_INCLUDE="-I$dir/include/openssl -I$dir/include" - /bin/rm ${ERL_TOP}/lib/ssl/SKIP - /bin/rm ${ERL_TOP}/lib/crypto/SKIP - - - fi - done + echo "I could not find ssleay" > ${ERL_TOP}/lib/ssl/SKIP + echo "I could not find ssleay" > ${ERL_TOP}/lib/crypto/SKIP fi - - - #-------------------------------------------------------------------- # Os mon stuff. #-------------------------------------------------------------------- echo $ac_n "checking for kstat_open in -lkstat""... $ac_c" 1>&6 -echo "configure:4486: checking for kstat_open in -lkstat" >&5 +echo "configure:4436: checking for kstat_open in -lkstat" >&5 ac_lib_var=`echo kstat'_'kstat_open | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 @@ -4490,7 +4440,7 @@ ac_save_LIBS="$LIBS" LIBS="-lkstat $LIBS" cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then +if { (eval echo configure:4455: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else @@ -4532,7 +4482,7 @@ # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:4536: checking for $ac_word" >&5 +echo "configure:4486: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_JAVAC'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -4563,7 +4513,7 @@ if test -n "$JAVAC"; then echo $ac_n "checking for JDK version 1.2""... $ac_c" 1>&6 -echo "configure:4567: checking for JDK version 1.2" >&5 +echo "configure:4517: checking for JDK version 1.2" >&5 if eval "test \"`echo '$''{'ac_cv_prog_javac_ver_1_2'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -4574,7 +4524,7 @@ java.util.Iterator iter; ; return; }} EOF -if { (eval echo configure:4578: \"$java_link\") 1>&5; (eval $java_link) 2>&5; } && test -s conftest.class; then +if { (eval echo configure:4528: \"$java_link\") 1>&5; (eval $java_link) 2>&5; } && test -s conftest.class; then rm -rf conftest* ac_cv_prog_javac_ver_1_2=yes else @@ -4608,7 +4558,7 @@ # Extract the first word of "g++", so it can be a program name with args. set dummy g++; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 -echo "configure:4612: checking for $ac_word" >&5 +echo "configure:4562: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_CXX'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else @@ -4861,7 +4811,6 @@ s%@DED_LD@%$DED_LD%g s%@DED_LDFLAGS@%$DED_LDFLAGS%g s%@ded_soname@%$ded_soname%g -s%@SSL_INCLUDE@%$SSL_INCLUDE%g s%@SSLEAY_ROOT@%$SSLEAY_ROOT%g s%@os_mon_programs@%$os_mon_programs%g s%@JAVAC@%$JAVAC%g --- lib/crypto/c_src/Makefile.in Sun Oct 1 10:30:28 2000 +++ lib/crypto/c_src/Makefile.in.ORIG Thu Sep 14 21:27:12 2000 @@ -48,11 +48,11 @@ SSLEAY_ROOT = @SSLEAY_ROOT@ SSLEAY_LIBDIR = $(SSLEAY_ROOT)/lib -SSLEAY_INCDIR = @SSL_INCLUDE@ +SSLEAY_INCDIR = $(SSLEAY_ROOT)/include # For erl_driver.h SYS_INCDIR = $(ERL_TOP)/erts/emulator/beam -INCLUDES = $(SSLEAY_INCDIR) -I $(SYS_INCDIR) +INCLUDES = -I $(SSLEAY_INCDIR) -I $(SYS_INCDIR) ifeq ($(TYPE),debug) TYPEMARKER = .debug --- lib/ssl/c_src/Makefile.in Sun Oct 1 10:35:19 2000 +++ lib/ssl/c_src/Makefile.in.ORIG Sun Oct 1 10:33:34 2000 @@ -41,8 +41,8 @@ # ---------------------------------------------------- # Includes and libs # ---------------------------------------------------- -SSLEAY_INCLUDE = @SSL_INCLUDE@ -SSLEAY_LIBDIR = $(SSLEAY_ROOT)/lib +SSLEAY_INCLUDE = -I $(SSLEAY_ROOT)/$(TARGET)/include +SSLEAY_LIBDIR = $(SSLEAY_ROOT)/$(TARGET)/lib ALL_CFLAGS = @CFLAGS@ @DEFS@ $(TYPE_FLAGS) TARGET = @host@ From geoff@REDACTED Sun Oct 1 06:34:53 2000 From: geoff@REDACTED (Geoff Wong) Date: Sun, 1 Oct 2000 15:34:53 +1100 (EST) Subject: stdup() patch for R7B. In-Reply-To: from "Kent Boortz" at Sep 29, 2000 02:35:44 PM Message-ID: <200010010434.EAA18631@gecko.serc.rmit.edu.au> HAVE_STRDUP isn't properly defined by the configure process (and isn't defined in header files on Redhat 6.x boxes). Anyway - this causes erl_connect (and associated libs) to fail to build. A patch follows. Geoff --- erts/autoconf/configure.in Sun Oct 1 15:27:11 2000 +++ erts/autoconf/configure.in.ORIG Sun Oct 1 10:17:38 2000 @@ -370,7 +370,7 @@ AC_CHECK_FUNCS([ieee_handler fpsetmask finite res_gethostbyname dlopen \ poll pread pwrite writev memmove strerror \ - gethrtime localtime_r gmtime_r strdup]) + gethrtime localtime_r gmtime_r]) AC_FUNC_SETVBUF_REVERSED AC_FUNC_VFORK AC_FUNC_VPRINTF --- erts/autoconf/configure Sun Oct 1 15:27:17 2000 +++ erts/autoconf/configure.ORIG Sun Oct 1 10:28:58 2000 @@ -1366,7 +1366,7 @@ done test -n "$YACC" || YACC="yacc" -for ac_prog in gawk mawk nawk awk +for ac_prog in mawk gawk nawk awk do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 @@ -3456,7 +3456,7 @@ for ac_func in ieee_handler fpsetmask finite res_gethostbyname dlopen \ poll pread pwrite writev memmove strerror \ - gethrtime localtime_r gmtime_r strdup + gethrtime localtime_r gmtime_r do echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 echo "configure:3463: checking for $ac_func" >&5 --- erts/autoconf/config.h.in Sun Oct 1 15:13:33 2000 +++ erts/autoconf/config.h.in.ORIG Sun Oct 1 15:36:10 2000 @@ -168,9 +168,6 @@ /* Define if you have the strerror function. */ #undef HAVE_STRERROR -/* Define if you have the strdup() function. */ -#undef HAVE_STRDUP - /* Define if you have the writev function. */ #undef HAVE_WRITEV From stimuli@REDACTED Sun Oct 1 07:50:59 2000 From: stimuli@REDACTED (Jeffrey Straszhiem) Date: Sun, 1 Oct 2000 01:50:59 -0400 Subject: stdup() patch for R7B. In-Reply-To: <200010010434.EAA18631@gecko.serc.rmit.edu.au> References: <200010010434.EAA18631@gecko.serc.rmit.edu.au> Message-ID: <20001001015059.A2756@bzzt.shadow.net> On Sun, Oct 01, 2000 at 03:34:53PM +1100, Geoff Wong wrote: > HAVE_STRDUP isn't properly defined by the configure process (and > isn't defined in header files on Redhat 6.x boxes). Anyway - this > causes erl_connect (and associated libs) to fail to build. > A patch follows. I seem to be having this same problem. I tried this patch, however, and it wouldn't work. I even tried applying it by hand, its being small enough. Anyhow, when I build I get this: make[4]: Entering directory `/opt/erlang.build/otp_src_R7B-0/lib/erl_interface/src' gcc -g -O2 -I/opt/erlang.build/otp_src_R7B-0/erts/autoconf/i686-pc-linux-gnu -fPIC -Wall -O2 -DDEBUG_DIST -I. -DHAVE_CONFIG_H -DUSE_DNS -DEPMD_PORT=4369 -c erl_connect.c -o /opt/erlang.build/otp_src_R7B-0/lib/erl_interface/obj/i686-pc-linux-gnu/erl_connect.o erl_connect.c:183: parse error before `__extension__' erl_connect.c:183: `__len' undeclared here (not in a function) erl_connect.c:183: initializer element is not constant erl_connect.c:183: parse error before `if' erl_connect.c:183: warning: type defaults to `int' in declaration of `__retval' erl_connect.c:183: conflicting types for `__retval' erl_connect.c:183: previous declaration of `__retval' erl_connect.c:183: warning: data definition has no type or storage class erl_connect.c:183: parse error before `}' Obviously something is going wrong. -- -- Jeffrey Straszheim | A sufficiently advanced -- Systems Engineer, Programmer | regular expression is -- http://www.shadow.net/~stimuli | indistinguishable from -- stimuli AT shadow DOT net | magic From geoff@REDACTED Sun Oct 1 08:27:24 2000 From: geoff@REDACTED (Geoff Wong) Date: Sun, 1 Oct 2000 17:27:24 +1100 (EST) Subject: stdup() patch for R7B. In-Reply-To: <20001001015059.A2756@bzzt.shadow.net> from "Jeffrey Straszhiem" at Oct 01, 2000 01:50:59 AM Message-ID: <200010010627.GAA19030@gecko.serc.rmit.edu.au> > > On Sun, Oct 01, 2000 at 03:34:53PM +1100, Geoff Wong wrote: > > > HAVE_STRDUP isn't properly defined by the configure process (and > > isn't defined in header files on Redhat 6.x boxes). Anyway - this > > causes erl_connect (and associated libs) to fail to build. > > > A patch follows. > > > > I seem to be having this same problem. I tried this patch, however, > and it wouldn't work. I even tried applying it by hand, its being > small enough. > > Anyhow, when I build I get this: > > make[4]: Entering directory `/opt/erlang.build/otp_src_R7B-0/lib/erl_interface/src' > gcc -g -O2 -I/opt/erlang.build/otp_src_R7B-0/erts/autoconf/i686-pc-linux-gnu -fPIC -Wall -O2 -DDEBUG_DIST -I. -DHAVE_CONFIG_H -DUSE_DNS -DEPMD_PORT=4369 -c erl_connect.c -o /opt/erlang.build/otp_src_R7B-0/lib/erl_interface/obj/i686-pc-linux-gnu/erl_connect.o > erl_connect.c:183: parse error before `__extension__' > erl_connect.c:183: `__len' undeclared here (not in a function) > erl_connect.c:183: initializer element is not constant > erl_connect.c:183: parse error before `if' > erl_connect.c:183: warning: type defaults to `int' in declaration of `__retval' > erl_connect.c:183: conflicting types for `__retval' > erl_connect.c:183: previous declaration of `__retval' > erl_connect.c:183: warning: data definition has no type or storage class > erl_connect.c:183: parse error before `}' > > > Obviously something is going wrong. Hmm - strange. I'm rebuilding from scratch to re-test my patch. [ My patch was a reverse patch - but you probably noticed that already ]. As a temporary measure you can edit lib/erl_interface/src/erl_connect.c and add in a #define HAVE_STRDUP 1 manually (about line 30). Geoff From vladdu@REDACTED Sun Oct 1 08:53:37 2000 From: vladdu@REDACTED (Vlad Dumitrescu) Date: Sun, 1 Oct 2000 08:53:37 +0200 Subject: port to QNX References: <200010010434.EAA18631@gecko.serc.rmit.edu.au> Message-ID: Hi! Since QNX Real Time Platform is now free to use for non-commercial purposes, I thought to try to compile Erlang R7B on the platform. It didn't work out-of-the-box (only natural, I think) and I realized that my knowledge when it comes to tweak configure and make scripts is not enough... Since QNX is fully POSIX compatible, the port itself should be more or less a no-op. Anyone did it or can tell me what to do? tia Vlad From vladdu@REDACTED Sun Oct 1 09:01:56 2000 From: vladdu@REDACTED (Vlad Dumitrescu) Date: Sun, 1 Oct 2000 09:01:56 +0200 Subject: QNX port References: <200010010627.GAA19030@gecko.serc.rmit.edu.au> Message-ID: I found the comments about porting Erlang 47.4.1, and that no autoconf tweak was needed. However, I can't make it work... - configure cannot autodetect host type. adding "host=QNX" makes it run, but complaining about unknown host... - then make seems not being able to find the Makefiles in each dir... any ideas? Vlad From geoff@REDACTED Sun Oct 1 14:03:10 2000 From: geoff@REDACTED (Geoff Wong) Date: Sun, 1 Oct 2000 23:03:10 +1100 (EST) Subject: RPM Spec for R7B. Message-ID: <200010011203.MAA20240@gecko.serc.rmit.edu.au> My RPM spec for Erlang R7B follows. The patches are the ones I've posted here today (packaged slightly differently; you'll have to tinker with them). I'll try to make a package available on the eddieware site sometime in the near future. Geoff ---- Summary: The Erlang/OTP system. Name: erlang_otp # ######################################## # Uncomment the define and Requires # for your intended RedHat version # ##### RedHat 5.2 ##### #%define rhrel 5.2 #Requires: redhat-release < 6.0 # ##### RedHat 6.0 ##### %define rhrel 6.1 Requires: redhat-release >= 6.0 ######################################## # Version: R7B Release: 1 %define SrcRelease 0 Copyright: Ericsson Public License Group: Networking/Utilities Source: http://www.erlang.org/download/otp_src_%{version}-%{SrcRelease}.tar.gz #Patch0: 7b_patch01.txt #Patch1: 7b_patch02.txt #Patch2: 7b_patch03.txt #Patch3: 7b_patch04.txt #Patch4: 7b_patch05.txt Provides: Open Source Erlang/OTP BuildRoot: /tmp/erlangbuild URL: http://www.erlang.org/ Packager: geoff@REDACTED %description The Erlang/OTP system --- Erlang is a programming language which has many features more commonly associated with an operating system than with a programming language: concurrent processes, scheduling, memory management, distribution, networking, etc. The development package in addition contains the Erlang sources for all base libraries. Includes the Erlang/OTP graphical libraries. NOTE: this RPM is intended for RedHat %{rhrel} systems. %prep rm -rf "$RPM_BUILD_ROOT" %setup -n otp_src_%{version}-0 patch -p0 < $RPM_SOURCE_DIR/7b_patch01.txt patch -p0 < $RPM_SOURCE_DIR/7b_patch02.txt patch -p0 < $RPM_SOURCE_DIR/7b_patch03.txt patch -p0 < $RPM_SOURCE_DIR/7b_patch04.txt patch -p0 < $RPM_SOURCE_DIR/7b_patch05.txt %build ./configure --prefix=/usr/local make %install rm -rf $RPM_BUILD_ROOT make install INSTALL_PREFIX="$RPM_BUILD_ROOT" sed s:$RPM_BUILD_ROOT:: $RPM_BUILD_ROOT/usr/local/lib/erlang/bin/erl > $RPM_BUILD_ROOT/usr/local/lib/erlang/bin/erl.inst mv $RPM_BUILD_ROOT/usr/local/lib/erlang/bin/erl $RPM_BUILD_ROOT/usr/local/lib/erlang/bin/erl.build mv $RPM_BUILD_ROOT/usr/local/lib/erlang/bin/erl.inst $RPM_BUILD_ROOT/usr/local/lib/erlang/bin/erl chmod a+x $RPM_BUILD_ROOT/usr/local/lib/erlang/bin/erl rm $RPM_BUILD_ROOT/usr/local/bin/erl rm $RPM_BUILD_ROOT/usr/local/bin/erlc cd $RPM_BUILD_ROOT/usr/local/bin; ln -s /usr/local/lib/erlang/bin/erl .; ln -s /usr/local/lib/erlang/bin/erlc . %clean rm -rf "$RPM_BUILD_ROOT" %files %defattr(644 root root 755) %attr( - ,root,root) /usr/local/bin %attr( - ,root,root) /usr/local/lib %changelog #* Sat Sep 30 Geoff Wong #- revamped spec file From vladdu@REDACTED Sun Oct 1 16:49:52 2000 From: vladdu@REDACTED (Vlad Dumitrescu) Date: Sun, 1 Oct 2000 16:49:52 +0200 Subject: QNX port Message-ID: I fiddled with the configure and configure.guess scripts, making them use 'i586-intel-none' as a host target. Here are the messages that make spits out, in hope they make sense to someone :-) thanks Vlad cd erts && ERL_TOP=/usr/local/otp_src_R7B-0 make NO_START_SCRIPTS=true opt make[1]: Entering directory `/usr/local/otp_src_R7B-0/erts' make[2]: Entering directory `/usr/local/otp_src_R7B-0/erts/emulator' make -f i586-intel-none/Makefile TYPE=opt make[3]: Entering directory `/usr/local/otp_src_R7B-0/erts/emulator' /usr/bin/perl utils/beam_makeops -outdir i586-intel-none -emulator \ /usr/local/otp_src_R7B-0/lib/compiler/src/genop.tab beam/ops.tab beam/ops.tab(759): too many arguments make[3]: *** [i586-intel-none/beam_opcodes.h] Error 255 make[3]: Leaving directory `/usr/local/otp_src_R7B-0/erts/emulator' make[2]: *** [opt] Error 2 make[2]: Leaving directory `/usr/local/otp_src_R7B-0/erts/emulator' make[2]: Entering directory `/usr/local/otp_src_R7B-0/erts/etc' make[3]: Entering directory `/usr/local/otp_src_R7B-0/erts/etc/common' make -f i586-intel-none/Makefile TYPE=opt make[4]: Entering directory `/usr/local/otp_src_R7B-0/erts/etc/common' make[4]: Nothing to be done for `etc'. make[4]: Leaving directory `/usr/local/otp_src_R7B-0/erts/etc/common' make[3]: Leaving directory `/usr/local/otp_src_R7B-0/erts/etc/common' make[2]: Leaving directory `/usr/local/otp_src_R7B-0/erts/etc' make[2]: Entering directory `/usr/local/otp_src_R7B-0/erts/epmd' make[3]: Entering directory `/usr/local/otp_src_R7B-0/erts/epmd/src' make -f i586-intel-none/Makefile TYPE=opt make[4]: Entering directory `/usr/local/otp_src_R7B-0/erts/epmd/src' gcc -g -O2 -I/usr/local/otp_src_R7B-0/erts/autoconf/i586-intel-none -DHAVE_CONF IG_H -O2 -Wall -DEPMD_PORT_NO=4369 -o /usr/local/otp_src_R7B-0/erts/obj/i586-intel-none/epmd.o -c epmd.c gcc -g -O2 -I/usr/local/otp_src_R7B-0/erts/autoconf/i586-intel-none -DHAVE_CONF IG_H -O2 -Wall -DEPMD_PORT_NO=4369 -o /usr/local/otp_src_R7B-0/erts/obj/i586-intel-none/epmd_cli.o -c epmd_cli.c gcc -g -O2 -I/usr/local/otp_src_R7B-0/erts/autoconf/i586-intel-none -DHAVE_CONF IG_H -O2 -Wall -DEPMD_PORT_NO=4369 -o /usr/local/otp_src_R7B-0/erts/obj/i586-intel-none/epmd_srv.o -c epmd_srv.c epmd_srv.c: In function `run': epmd_srv.c:227: warning: suggest explicit braces to avoid ambiguous `else' gcc -o /usr/local/otp_src_R7B-0/bin/i586-intel-none/epmd /usr/local/otp_src_R7B-0/erts/obj/i586-intel-none/epmd.o /usr/local/otp_src_R7B-0/erts/obj/i586-intel-none/epmd_cli.o /usr/local/otp_src_R7B-0/erts/obj/i586-intel-none/epmd_srv.o -lncurses -lm -lsocket make[4]: Leaving directory `/usr/local/otp_src_R7B-0/erts/epmd/src' make[3]: Leaving directory `/usr/local/otp_src_R7B-0/erts/epmd/src' make[2]: Leaving directory `/usr/local/otp_src_R7B-0/erts/epmd' make[1]: Leaving directory `/usr/local/otp_src_R7B-0/erts' cd lib && \ ERL_TOP=/usr/local/otp_src_R7B-0 PATH=/usr/local/otp_src_R7B-0/bootstrap/bin:${PATH} \ make opt BUILD_ALL=true make[1]: Entering directory `/usr/local/otp_src_R7B-0/lib' make[2]: Entering directory `/usr/local/otp_src_R7B-0/lib/stdlib' make[3]: Entering directory `/usr/local/otp_src_R7B-0/lib/stdlib/src' erlc -W -bbeam -I../include -I../../kernel/include -o../ebin beam_lib.erl erlc: Error 8 executing 'erl'. make[3]: *** [../ebin/beam_lib.beam] Error 1 make[3]: Leaving directory `/usr/local/otp_src_R7B-0/lib/stdlib/src' make[2]: *** [opt] Error 2 make[2]: Leaving directory `/usr/local/otp_src_R7B-0/lib/stdlib' make[1]: *** [opt] Error 2 make[1]: Leaving directory `/usr/local/otp_src_R7B-0/lib' make: *** [libs] Error 2 From thantos@REDACTED Sun Oct 1 17:19:24 2000 From: thantos@REDACTED (Alexander Williams) Date: Sun, 1 Oct 2000 11:19:24 -0400 Subject: Alpha/Linux Port In-Reply-To: ; from vladdu@hotmail.com on Sun, Oct 01, 2000 at 04:49:52PM +0200 References: Message-ID: <20001001111924.A781@gw.total-web.net> While we're discussing ports ... I actually made the attempt to compile Erlang R7B on an Alpha 300 running SuSE 7.0; predictibly, it failed, but it only seemed to fail for a couple source files for BEAM. I can ravage through the backdoor and pull the make log if there's a porter working on making it 64-bit clean ... -- Alexander Williams (thantos@REDACTED) | In the End, "Blue Jester needs food." | Oblivion "Blue Jester needs fuku-wearing cuties." | Always http://www.chancel.org | Wins From Sean.Hinde@REDACTED Sun Oct 1 19:47:33 2000 From: Sean.Hinde@REDACTED (Sean Hinde) Date: Sun, 1 Oct 2000 18:47:33 +0100 Subject: QNX port Message-ID: <402DD461F109D411977E0008C791C312565198@imp02mbx.one2one.co.uk> > erlc -W -bbeam -I../include -I../../kernel/include -o../ebin > beam_lib.erl > erlc: Error 8 executing 'erl'. > make[3]: *** [../ebin/beam_lib.beam] Error 1 This would appear to be a bug in the QNX system. erlc uses an execvp call to start the erl shell script, which according to their docs is supposed to work correctly on shell scripts as well as normal executables. Error 8 means duff executable which suggests something is broken in QNX (errno 8 at the QNX shell gives a description - very nice). I have made QNX aware of this and am waiting for a response. I've not really got any further than this as the moment except.. In R7A distribution didn't work properly because SO_ERROR is defined by QNX but appears not to be actually implemented. My fix was to change the #ifndef SO_ERROR in inet_drv.c back to what it was in R6B thereby avoiding the problem. I also got hold of the latest config.guess and config.sub from what I seem to remember was the autoconf homepage - these have all the correct defines for QNX. If I get any further I'll let you know. Regards, Sean NOTICE AND DISCLAIMER: This email (including attachments) is confidential. If you have received this email in error please notify the sender immediately and delete this email from your system without copying or disseminating it or placing any reliance upon its contents. We cannot accept liability for any breaches of confidence arising through use of email. Any opinions expressed in this email (including attachments) are those of the author and do not necessarily reflect our opinions. We will not accept responsibility for any commitments made by our employees outside the scope of our business. We do not warrant the accuracy or completeness of such information. From jamesh@REDACTED Sun Oct 1 21:10:17 2000 From: jamesh@REDACTED (James Hague) Date: Sun, 01 Oct 2000 14:10:17 -0500 Subject: Small stand-alone applications Message-ID: <3.0.32.20001001141017.00ff6740@volition-inc.com> What happened to the small stand-alone application stuff that Joe Armstrong was working on? There was a working prototype available for R6. Was this continued or dropped for R7? James From vladdu@REDACTED Sun Oct 1 22:33:18 2000 From: vladdu@REDACTED (Vlad Dumitrescu) Date: Sun, 1 Oct 2000 22:33:18 +0200 Subject: QNX port References: <402DD461F109D411977E0008C791C312565198@imp02mbx.one2one.co.uk> Message-ID: > I have made QNX aware of this and am waiting for a response. I've not really > got any further than this as the moment except.. > If I get any further I'll let you know. Thanks. Are you using QNX 4 or the new RTP? Maybe they fixed the bug in the new release... Vlad From davidg@REDACTED Mon Oct 2 08:56:18 2000 From: davidg@REDACTED (David Gould) Date: Sun, 1 Oct 2000 23:56:18 -0700 Subject: Updated BEAM spec? In-Reply-To: <3.0.32.20000815110845.00f34544@volition-inc.com>; from James Hague on Tue, Aug 15, 2000 at 11:08:45AM -0500 References: <3.0.32.20000815110845.00f34544@volition-inc.com> Message-ID: <20001001235618.B18911@dnai.com> On Tue, Aug 15, 2000 at 11:08:45AM -0500, James Hague wrote: ... > Thank you for the reply. Which part of the compiler/assembler would be > best for seeing a list of generic beam opcodes and operand formats? I > wrote a quickie utility a while back to take apart beam files, but not > inside of the code chunk. ... Hmm, just going through my old mail and saw this. Would you care to share your "quickie utility a while back to take apart beam files"? It sounds interesting. Thanks -dg -- David Gould davidg@REDACTED 510.536.1443 also dg@REDACTED 510.628.3380 - If simplicity worked, the world would be overrun with insects. - From qramika@REDACTED Mon Oct 2 10:42:50 2000 From: qramika@REDACTED (Karlsson Mikael) Date: Mon, 2 Oct 2000 10:42:50 +0200 (MET DST) Subject: space and Binary syntax Message-ID: <200010020843.KAA23733@nic.era-a.ericsson.se> How should space characters be treated in the Binary syntax? I get the following results from within the Erlang shell: Eshell V5.0.1 (abort with ^G) 1> Bin=<<8,16>>. ** 1: syntax error before: '<' ** 1> Bin =<<8,16>>. ** 1: syntax error before: '<' ** 1> Bin= <<8,16>>. <<8,16>> 2> Bin2 = <<8,16>>. <<8,16>> /Mikael Karlsson From Sean.Hinde@REDACTED Mon Oct 2 10:50:10 2000 From: Sean.Hinde@REDACTED (Sean Hinde) Date: Mon, 2 Oct 2000 09:50:10 +0100 Subject: QNX port Message-ID: <402DD461F109D411977E0008C791C3125651A0@imp02mbx.one2one.co.uk> It's the new RTP Sean > -----Original Message----- > From: Vlad Dumitrescu [mailto:vladdu@REDACTED] > Sent: 1 October 2000 21:33 > To: erlang-questions@REDACTED > Subject: Re: QNX port > > > > I have made QNX aware of this and am waiting for a > response. I've not really > > got any further than this as the moment except.. > > > If I get any further I'll let you know. > > Thanks. Are you using QNX 4 or the new RTP? Maybe they fixed > the bug in the new release... > Vlad > NOTICE AND DISCLAIMER: This email (including attachments) is confidential. If you have received this email in error please notify the sender immediately and delete this email from your system without copying or disseminating it or placing any reliance upon its contents. We cannot accept liability for any breaches of confidence arising through use of email. Any opinions expressed in this email (including attachments) are those of the author and do not necessarily reflect our opinions. We will not accept responsibility for any commitments made by our employees outside the scope of our business. We do not warrant the accuracy or completeness of such information. From qramika@REDACTED Mon Oct 2 11:05:43 2000 From: qramika@REDACTED (Karlsson Mikael) Date: Mon, 2 Oct 2000 11:05:43 +0200 (MET DST) Subject: space and Binary syntax Message-ID: <200010020905.LAA24579@nic.era-a.ericsson.se> OOPS, sorry should have read the Ch. 6.2 A lexical note in "Erlang Extensions Since 4.4" >> Note that "B=<<1>>" will be interpreted as "B =< ;<1>>", which is a syntax error. The correct way to write the expression is "B = <<1>>". << /Mikael > Date: Mon, 2 Oct 2000 10:42:50 +0200 (MET DST) > From: Karlsson Mikael > Subject: space and Binary syntax > To: erlang-questions@REDACTED > MIME-Version: 1.0 > Content-MD5: +NIKEOgEhZRvOvL3CALaGw== > > How should space characters be treated in the > Binary syntax? > I get the following results from within the Erlang > shell: > > Eshell V5.0.1 (abort with ^G) > 1> Bin=<<8,16>>. > ** 1: syntax error before: '<' ** > 1> Bin =<<8,16>>. > ** 1: syntax error before: '<' ** > 1> Bin= <<8,16>>. > <<8,16>> > 2> Bin2 = <<8,16>>. > <<8,16>> > > /Mikael Karlsson > From sabry@REDACTED Mon Oct 2 16:20:19 2000 From: sabry@REDACTED (Amr Sabry) Date: Mon, 02 Oct 2000 09:20:19 -0500 Subject: Extended Submission Deadline (Continuations Workshop CW'01) Message-ID: <200010021420.JAA17172@dogfish.cs.indiana.edu> The Third ACM SIGPLAN Workshop on Continuations (CW'01) London, England, Jan. 16, 2001 Collocated with POPL '01 (Jan. 17, 2001 -- Jan. 19, 2001) http://www.cs.indiana.edu/~sabry/cw01/ We have extended the submission deadline for CW'01 until October 15! See the web page for details. --Amr From stimuli@REDACTED Sat Oct 7 01:17:56 2000 From: stimuli@REDACTED (Jeffrey Straszhiem) Date: Fri, 6 Oct 2000 19:17:56 -0400 Subject: Suggested Example Message-ID: <20001006191756.A975@bzzt.shadow.net> Hi, Yesterday I read the new document up on the Erlang site regarding the experiences of the Sendmail folks. One point I found interesting, and which matches my own experience, is their difficulty figuring out the OTP framework for applications, supervisors, gen_server's and such. I've read the Erlang book and I basically understanding how to use the language. I've also read through most of the OTP docs, and I think I understand most of what I read, but I still can't see the forest for the trees. I get the basic idea of setting up supervisors and worker processes, but there are many, many details which are not obvious from the docs. A tutorial on building an OTP application would be nice. Including some design advice, structuring the supervisors, putting together some basic generic behaviors, a error tracking and logging discipline, and so forth. Also it would be great if this example used one or two of the more important parts of OTP, such as Mnesia and SASL. I understand the difficulty of putting a tutorial like this together, and don't expect to see anything on that order soon. However, it would be nice to have a simple, but fairly complete, Erlang application to look at -- and by that I mean a skeleton of one. I know I could look at Mnesia or something, but I'd rather focus all of my meager understanding on figuring out the OTP structures instead of the specifics of the database. Any suggestions? -- Jeffrey Straszheim | A sufficiently advanced -- Systems Engineer, Programmer | regular expression is -- http://www.shadow.net/~stimuli | indistinguishable from -- stimuli AT shadow DOT net | magic From doug+ml.erlang@REDACTED Sun Oct 8 03:02:28 2000 From: doug+ml.erlang@REDACTED (Doug Bagley) Date: 07 Oct 2000 20:02:28 -0500 Subject: How to do line oriented i/o in constant space? Message-ID: I wanted to write a small version of the Unix 'cat' command in Erlang. The following program works, but it uses memory in proportion to the input data size (choosing either the cat1 or cat2 function). It looks like it should take advantage of tail recursion, but I must admit I can be easily fooled since I'm new to functional programming and Erlang. What would be the best way to do I/O in constant space in Erlang? Thanks, Doug %%% -*- mode: erlang -*- % cat.erl % instructions: % erl -compile cat.erl % cat datafile | erl -noshell -s cat main -module(cat). -export([main/0]). main() -> %cat1(io:get_line('')), cat2(io:get_line('')), halt(0). cat1(Line) -> case Line of eof -> true; _ -> io:put_chars(Line), cat1(io:get_line('')) end. cat2(eof) -> true; cat2(Line) -> io:put_chars(Line), cat2(io:get_line('')). From etxuwig@REDACTED Mon Oct 9 11:38:35 2000 From: etxuwig@REDACTED (Ulf Wiger) Date: Mon, 9 Oct 2000 11:38:35 +0200 (MET DST) Subject: Suggested Example In-Reply-To: <20001006191756.A975@bzzt.shadow.net> Message-ID: On Fri, 6 Oct 2000, Jeffrey Straszhiem wrote: > >Hi, > >Yesterday I read the new document up on the Erlang site regarding >the experiences of the Sendmail folks. One point I found >interesting, and which matches my own experience, is their >difficulty figuring out the OTP framework for applications, >supervisors, gen_server's and such. I agree that this is a problem. >I understand the difficulty of putting a tutorial like this >together, and don't expect to see anything on that order soon. >However, it would be nice to have a simple, but fairly complete, >Erlang application to look at -- and by that I mean a skeleton of >one. I know I could look at Mnesia or something, but I'd rather >focus all of my meager understanding on figuring out the OTP >structures instead of the specifics of the database. > >Any suggestions? There are some applications in OTP that could be modified into good examples of how to build an application. I thought os_mon might be a pretty good example, because it's not that complicated. Unfortunately, the os_mon.erl module implements both the application behaviour and the supervisor behaviour. This is cheating, and probably confusing to the beginner. It would not be difficult to change this. /Uffe -- Ulf Wiger tfn: +46 8 719 81 95 Network Architecture & Product Strategies mob: +46 70 519 81 95 Ericsson Telecom AB, Datacom Networks and IP Services Varuv?gen 9, ?lvsj?, S-126 25 Stockholm, Sweden From ltaesch@REDACTED Sat Oct 7 13:34:49 2000 From: ltaesch@REDACTED (Luc Taesch) Date: Sat, 07 Oct 2000 13:34:49 +0200 Subject: compiler available for x86 ?. Message-ID: <39DF0A59.B39330D@europemail.com> i once asked which of compiler is available for x86. hipe is for sparc, and etos link is still un-available. From Sean.Hinde@REDACTED Mon Oct 9 11:50:29 2000 From: Sean.Hinde@REDACTED (Sean Hinde) Date: Mon, 9 Oct 2000 10:50:29 +0100 Subject: Suggested Example Message-ID: <402DD461F109D411977E0008C791C3125651B8@imp02mbx.one2one.co.uk> > I've read the Erlang book and I basically understanding how to use the > language. I've also read through most of the OTP docs, and I think I > understand most of what I read, but I still can't see the forest for > the trees. I get the basic idea of setting up supervisors and worker > processes, but there are many, many details which are not obvious from > the docs. I agree, and it caused me a lot of pain also. Now that I've figured it out though I find it quick and easy to produce a new application using the skeletons in emacs. Just create an application skeleton, a supervisor skeleton, and your gen_server skeletons. Fill out the missing fields in each file and you are almost there. All that is needed is an application_name.app file in your ebin directory and you can call application:start(name). and you are away. Obviously there is more to it than this but perserverence does pay!! Sean NOTICE AND DISCLAIMER: This email (including attachments) is confidential. If you have received this email in error please notify the sender immediately and delete this email from your system without copying or disseminating it or placing any reliance upon its contents. We cannot accept liability for any breaches of confidence arising through use of email. Any opinions expressed in this email (including attachments) are those of the author and do not necessarily reflect our opinions. We will not accept responsibility for any commitments made by our employees outside the scope of our business. We do not warrant the accuracy or completeness of such information. From tobbe@REDACTED Mon Oct 9 12:52:51 2000 From: tobbe@REDACTED (Torbjorn Tornkvist) Date: 09 Oct 2000 12:52:51 +0200 Subject: How to do line oriented i/o in constant space? In-Reply-To: Doug Bagley's message of "07 Oct 2000 20:02:28 -0500" References: Message-ID: -module(pipe). -compile(export_all). %%--------------------------------------------------- %% # cat indata | erl -s pipe -nouser > outdata %% 1 2 3 4 5 %% 5 4 3 2 1 %% 5 1 2 3 4 %% 4 3 2 1 5 %%--------------------------------------------------- start() -> spawn(pipe,init,[]). init() -> Port = open_port({fd, 0, 1},[eof]), loop(Port). loop(Port) -> receive {Port, {data, What}} -> Port ! {self(), {command, What}}, loop(Port); {Port, eof} -> halt() end. From rv@REDACTED Mon Oct 9 13:30:03 2000 From: rv@REDACTED (Robert Virding) Date: Mon, 09 Oct 2000 13:30:03 +0200 Subject: How to do line oriented i/o in constant space? In-Reply-To: Your message of "07 Oct 2000 20:02:28 CDT." Message-ID: <200010091130.NAA24256@trana.bluetail.com> Doug Bagley writes: >I wanted to write a small version of the Unix 'cat' command in Erlang. >The following program works, but it uses memory in proportion to the >input data size (choosing either the cat1 or cat2 function). It looks >like it should take advantage of tail recursion, but I must admit I can >be easily fooled since I'm new to functional programming and Erlang. It most definitely should work in constant space as both functions are tailrecursive. In R7B cat1/1 and cat2/1 are completely equivalent (compiled code wil be the same) and which form you choose is a matter of taste. >What would be the best way to do I/O in constant space in Erlang? Depends what you want to do with the data. The method Tobbe shows is MUCH better if you just want to shuffle data through the system, getting lists is better if you want to process the data. A middle way would be to get binaries and then convert to lists as required. Robert -- Robert Virding Tel: +46 (0)8 545 55 017 Bluetail AB Email: rv@REDACTED S:t Eriksgatan 44 WWW: http://www.bluetail.com/~rv SE-112 32 Stockholm, SWEDEN "Folk s?ger att jag inte bryr mig om n?gonting, men det skiter jag i". >%%% -*- mode: erlang -*- >% cat.erl >% instructions: >% erl -compile cat.erl >% cat datafile | erl -noshell -s cat main > >-module(cat). >-export([main/0]). > >main() -> > %cat1(io:get_line('')), > cat2(io:get_line('')), > halt(0). > >cat1(Line) -> > case Line of > eof -> true; > _ -> io:put_chars(Line), > cat1(io:get_line('')) > end. > >cat2(eof) -> > true; >cat2(Line) -> > io:put_chars(Line), > cat2(io:get_line('')). > From amaewal@REDACTED Sat Oct 7 21:56:19 2000 From: amaewal@REDACTED (akhilesh maewal) Date: Sat, 7 Oct 2000 12:56:19 -0700 (PDT) Subject: Need Erlang help for novice. Message-ID: <20001007195619.8960.qmail@web1804.mail.yahoo.com> I have downloaded the latest version of Erlang. When I start the shell and give some commands, the following lines describe what I get. ------------------------------------------------------- Erlang (BEAM) emulator version 5.0.1 [threads] Eshell V5.0.1 (abort with ^G) 1> a=2. ** exited: {{badmatch,2},[{erl_eval,expr,3}]} ** 2> a="testing". ** exited: {{badmatch,"testing"},[{erl_eval,expr,3}]} ** 3> -------------------------------------------------- Could any one help me understand why I am getting these errors? Thank you very much. __________________________________________________ Do You Yahoo!? Yahoo! Photos - 35mm Quality Prints, Now Get 15 Free! http://photos.yahoo.com/ From raimo@REDACTED Mon Oct 9 14:34:44 2000 From: raimo@REDACTED (Raimo Niskanen) Date: Mon, 09 Oct 2000 14:34:44 +0200 Subject: Need Erlang help for novice. References: <20001007195619.8960.qmail@web1804.mail.yahoo.com> Message-ID: <39E1BB64.D675A477@erix.ericsson.se> Now, this was a real novice question, and here is the answer. The '=' operator in Erlang is not an assignment operator, but a pattern match operator. You tried to match the atom 'a' with the integer '2', which failed, and then the atom 'a' with the list of characters "testing", which also failed. Variables in Erlang all start with an uppercase letter, so if You try: 1> A = 2. it will succeed, but then 2> A = "testing". will fail because A is already bound to 2, and a variable can only be bound once. Variables are often bound as function arguments, which is useful, since the only way to "change" a variable is to calculate a new value and use it when calling some function. This is the essense of functional languages. For an introduction read the first chapter of the book "Concurrent Programming in Erlang" from the URL http://erlang.ericsson.se/doc/index.shtml. / Raimo Niskanen, Erlang/OTP akhilesh maewal wrote: > > I have downloaded the latest version of Erlang. When I > start the shell and give some commands, the following > lines describe what I get. > ------------------------------------------------------- > Erlang (BEAM) emulator version 5.0.1 [threads] > > Eshell V5.0.1 (abort with ^G) > 1> a=2. > ** exited: {{badmatch,2},[{erl_eval,expr,3}]} ** > 2> a="testing". > ** exited: {{badmatch,"testing"},[{erl_eval,expr,3}]} > ** > 3> > -------------------------------------------------- > > Could any one help me understand why I am getting > these errors? > > Thank you very much. > > __________________________________________________ > Do You Yahoo!? > Yahoo! Photos - 35mm Quality Prints, Now Get 15 Free! > http://photos.yahoo.com/ From venneri@REDACTED Mon Oct 9 20:19:30 2000 From: venneri@REDACTED (b.venneri) Date: Mon, 9 Oct 2000 14:19:30 -0400 Subject: PLI 2001-Call for workshop proposals Message-ID: CALL FOR WORKSHOP PROPOSALS Principles, Logics and Implementations of high-level programming languages (PLI 2001) Firenze, Italy September 3 - 7, 2001 http://music.dsi.unifi.it/pli01 PLI 2001, a federation of colloquia which includes ICFP 2001 (ACM-SIGPLAN International Conference on Functional Programming) and PPDP 2001 (ACM-SIGPLAN International Conference on Principles and Practice of Declarative Programming), will be held in Firenze, Italy, September 3 -7 2001. Affiliated workshops will be scheduled from September 2 through September 8. Researchers and practitioners are invited to submit workshop proposals, that may be sent to the PLI 2001 Workshop Chair Betti Venneri, venneri@REDACTED, by e-mail (Postscript, Pdf, ASCII) with "PLI01 Workshop Submission" in the subject header. Proposals should include * a short scientific justification of the proposed topic (somehow related to the colloquia), * names and contact information of the organizers, * expected number of participants and duration (the preference is for one day-long workshops), and any other relevant information (e.g., invited speakers, publication policy, etc.). THE DEADLINE FOR RECEIPT OF PROPOSALS IS JANUARY 8, 2001. Proposals will be evaluated by the PLI 2001 Workshop Chair, the ICFP and PPDP Program Chairs and Conference Chairs. Notification of acceptance will be made by February 2, 2001. Workshop selection committee: Xavier Leroy (INRIA, France), ICFP 2001 Program Chair Benjamin C. Pierce (Univ. of Pennsylvania), ICFP 2001 Conference Chair Harald Sondergaard (Univ. of Melbourne), PPDP 2001 Program Chair Rocco De Nicola (Univ. of Firenze), PPDP 2001 Conference Chair Betti Venneri (Univ. of Firenze), PLI 2001 Workshop Chair. web page: http://music.dsi.unifi.it/pli01/wkshops From klacke@REDACTED Mon Oct 9 15:55:22 2000 From: klacke@REDACTED (Klacke) Date: Mon, 9 Oct 2000 15:55:22 +0200 Subject: How to do line oriented i/o in constant space? In-Reply-To: <200010091130.NAA24256@trana.bluetail.com> References: <200010091130.NAA24256@trana.bluetail.com> Message-ID: <20001009155522.A5890@bluetail.com> > Depends what you want to do with the data. The method Tobbe shows is > MUCH better if you just want to shuffle data through the system, getting > lists is better if you want to process the data. Frankly I don't know if even this is recomendable even if we want to process the data. Processing 50k lines gives a beam emulator with some 55 Meg size which is hardly acceptable. The implementation of the io: routines are simply flawed and not efficent enough for this kind of tasks. > A middle way would be > to get binaries and then convert to lists as required. > Possibly. -- Claes Wikstrom Bluetail AB http://www.bluetail.com From Sean.Hinde@REDACTED Mon Oct 9 16:39:19 2000 From: Sean.Hinde@REDACTED (Sean Hinde) Date: Mon, 9 Oct 2000 15:39:19 +0100 Subject: How to do line oriented i/o in constant space? Message-ID: <402DD461F109D411977E0008C791C3125651BB@imp02mbx.one2one.co.uk> > > A middle way would be > > to get binaries and then convert to lists as required. > > > > > Possibly. So what would be the efficient way to do this? Sean NOTICE AND DISCLAIMER: This email (including attachments) is confidential. If you have received this email in error please notify the sender immediately and delete this email from your system without copying or disseminating it or placing any reliance upon its contents. We cannot accept liability for any breaches of confidence arising through use of email. Any opinions expressed in this email (including attachments) are those of the author and do not necessarily reflect our opinions. We will not accept responsibility for any commitments made by our employees outside the scope of our business. We do not warrant the accuracy or completeness of such information. From Sean.Hinde@REDACTED Mon Oct 9 16:40:18 2000 From: Sean.Hinde@REDACTED (Sean Hinde) Date: Mon, 9 Oct 2000 15:40:18 +0100 Subject: compiler available for x86 ?. Message-ID: <402DD461F109D411977E0008C791C3125651BC@imp02mbx.one2one.co.uk> I guess you are talking about Linux, in which case the normal R7B etc release works fine. It doesn't do native code compilation but beam compiled code is fast enough for most things. Maybe I missed the point of your question? Sean > -----Original Message----- > From: Luc Taesch [mailto:ltaesch@REDACTED] > Sent: 7 October 2000 12:35 > To: erlang-questions@REDACTED > Subject: compiler available for x86 ?. > > > i once asked which of compiler is available for x86. > hipe is for sparc, and etos link is still un-available. > > NOTICE AND DISCLAIMER: This email (including attachments) is confidential. If you have received this email in error please notify the sender immediately and delete this email from your system without copying or disseminating it or placing any reliance upon its contents. We cannot accept liability for any breaches of confidence arising through use of email. Any opinions expressed in this email (including attachments) are those of the author and do not necessarily reflect our opinions. We will not accept responsibility for any commitments made by our employees outside the scope of our business. We do not warrant the accuracy or completeness of such information. From ltaesch@REDACTED Sun Oct 8 22:51:45 2000 From: ltaesch@REDACTED (Luc Taesch) Date: Sun, 08 Oct 2000 22:51:45 +0200 Subject: compiler available for x86 ?. References: <402DD461F109D411977E0008C791C3125651BC@imp02mbx.one2one.co.uk> Message-ID: <39E0DE61.14B1C7D7@europemail.com> Sean Hinde wrote: > I guess you are talking about Linux, in which case the normal R7B etc > release works fine. It doesn't do native code compilation but beam compiled > code is fast enough for most things. > > Maybe I missed the point of your question? > sorry for being so imprecise. yes, im interesting in distributing a binary only. for convenience. say 1) i wanna deploy 30 times the same application, on client servers, and i dont wanna bother intalling erlang, or 2) i have access to a server i cant impose to have erlang installed (say sourceforge) 3) or i wanna build utilitaries, and i dont wanna be troubled with release conflicts as time goes by. 4) a demo softwarein a rpm, and i dont want bothering people downloading 8 mo of environement, or they cant be root ( to install erlang) these are different points where i may be interested to build a self contained binary. so i was looking at how to do that. and which constraints it may brings, like language restrictions. (and yes, R7b is quick enough for me!) > > Sean > > > -----Original Message----- > > From: Luc Taesch [mailto:ltaesch@REDACTED] > > Sent: 7 October 2000 12:35 > > To: erlang-questions@REDACTED > > Subject: compiler available for x86 ?. > > > > > > i once asked which of compiler is available for x86. > > hipe is for sparc, and etos link is still un-available. > > > > > > -- First, they ignore you. Then, they laugh at you. Then, they fight you. Then, you win. --- Gandhi. Working code is what matter, not your market capitalization. --Kurt granroth -------------- next part -------------- An HTML attachment was scrubbed... URL: From ltaesch@REDACTED Sun Oct 8 23:22:41 2000 From: ltaesch@REDACTED (Luc Taesch) Date: Sun, 08 Oct 2000 23:22:41 +0200 Subject: Suggested Example/ emacs questions References: <402DD461F109D411977E0008C791C3125651B8@imp02mbx.one2one.co.uk> Message-ID: <39E0E5A1.F9A0A5B5@europemail.com> Sean Hinde wrote: > > I've read the Erlang book and I basically understanding how to use the > > language. I've also read through most of the OTP docs, and I think I > > understand most of what I read, but I still can't see the forest for > > the trees. I get the basic idea of setting up supervisors and worker > > processes, but there are many, many details which are not obvious from > > the docs. > > I agree, and it caused me a lot of pain also. same for me . havent grok everything yet. but thats a big steps between a language, and an architecture..., probably the latter is not so easy to explain or justify, as it rely on a lot of implicit , undocumented assertions, that some call experience, and which is very subjective... > Now that I've figured it out > though I find it quick and easy to produce a new application using the > skeletons in emacs. that was in fact my point. on mandrake 7.1/7.2 and +, the skeletons deosnt works. i got the message symbol's fnction definition is void: deactivate-mark. (working in previous build) i suspect its an emacs packaing issue, but in case somebody knows something about it --- also , what is this erlang-apwiz.el in r7b ? any nice wizardry ? > -- First, they ignore you. Then, they laugh at you. Then, they fight you. Then, you win. --- Gandhi. Working code is what matter, not your market capitalization. --Kurt granroth -------------- next part -------------- An HTML attachment was scrubbed... URL: From sgelkins@REDACTED Tue Oct 10 05:15:32 2000 From: sgelkins@REDACTED (Steve Elkins) Date: Mon, 09 Oct 2000 23:15:32 -0400 Subject: Suggested Example References: Message-ID: <39E289D4.5F49DB77@bellsouth.net> Ulf Wiger wrote: > > On Fri, 6 Oct 2000, Jeffrey Straszhiem wrote: ... > >difficulty figuring out the OTP framework for applications, > >supervisors, gen_server's and such. > > I agree that this is a problem. > > >I understand the difficulty of putting a tutorial like this > >together, and don't expect to see anything on that order soon. > >However, it would be nice to have a simple, but fairly complete, > >Erlang application to look at -- and by that I mean a skeleton of > >one. I know I could look at Mnesia or something, but I'd rather > >focus all of my meager understanding on figuring out the OTP > >structures instead of the specifics of the database. Ok, I've figured out most of these issues and have attached a tgz containing 2 embarrassingly simple apps, as well as a perl script from the Cookbook that exercises one of them. One app, dshb, does nothing at all interesting. Skeletal. ;-) The other, cosb, is slightly more complicated. It has a special process that listens on a UDP port and notifies a gen_event example when it receives something. The event handler replies. Simple. To play with it... 1. tar zxvf ctii.tgz 2. cd ctii 3. erl -sname xyz -boot start_sasl -pa ./cosb/ebin -pa ./dshb/ebin 4. application:start(cosb). 5. udpmsg localhost hi 6. if all's well, udpmsg should say it received "you said hi" 7. udp_spc.erl is a standalone echoer, straight from the Special Processes chapter of Design Principles. ...assuming everyone can infer each step's context. Summing up, dshb is a very skeletal app, cosb has a little meat, and udp_spc is the forerunner of cosb. Supplemental reading would be chapters 5, 7, and 8 of Design Principles, as well as the relevant man pages. And sys:trace/2 is wonderful. Great, so I can build *local* apps. What's busting *my* chops is getting these apps to run distributed. I could really use something as simple as what I've provided here, but distributed. The particular place I'm stuck right now is getting a proper .boot together, since various messages in the archives have led me to think I can't just do something like step 4 above and hope for distribution, takeover, etc. systools:script2boot/1 says ok, but when I try... sge:219$ erl -boot start_cosb {'init terminating in do_boot',{'can not load',erlang,get_file}} init terminating in do_boot () sge:220$ I even ripped out all my stuff so my .script looks like the start_sasl one, but still the error. Is it where I'm doing script2boot? I realize this is only the 1st hurdle. Any advice at all on the general subject of building distributed apps would be greatly appreciated. Thanks, Steve -------------- next part -------------- A non-text attachment was scrubbed... Name: ctii.tgz Type: application/octet-stream Size: 18121 bytes Desc: not available URL: -------------- next part -------------- #!/usr/bin/perl -w # udpmsg - send a message to the udpquotd server use IO::Socket; use strict; my($sock, $server_host, $msg, $port, $ipaddr, $hishost, $MAXLEN, $PORTNO, $TIMEOUT); $MAXLEN = 1024; $PORTNO = 5151; $TIMEOUT = 5; $server_host = shift; $msg = "@ARGV"; $sock = IO::Socket::INET->new(Proto => 'udp', PeerPort => $PORTNO, PeerAddr => $server_host) or die "Creating socket: $!\n"; $sock->send($msg) or die "send: $!"; eval { local $SIG{ALRM} = sub { die "alarm time out" }; alarm $TIMEOUT; $sock->recv($msg, $MAXLEN) or die "recv: $!"; alarm 0; 1; # return value from eval on normalcy } or die "recv from $server_host timed out after $TIMEOUT seconds.\n"; ($port, $ipaddr) = sockaddr_in($sock->peername); $hishost = gethostbyaddr($ipaddr, AF_INET); print "Server $hishost responded ``$msg''\n"; From matthias@REDACTED Tue Oct 10 10:55:24 2000 From: matthias@REDACTED (matthias@REDACTED) Date: Tue, 10 Oct 2000 10:55:24 +0200 (CEST) Subject: Suggested Example/ emacs questions In-Reply-To: <39E0E5A1.F9A0A5B5@europemail.com> References: <402DD461F109D411977E0008C791C3125651B8@imp02mbx.one2one.co.uk> <39E0E5A1.F9A0A5B5@europemail.com> Message-ID: <14818.55676.460593.617173@corelatus.com> Others wrote: > > > I've read the Erlang book and I basically understanding how to use the > > > language. I've also read through most of the OTP docs, and I think I > > I agree, and it caused me a lot of pain also. Luc Taesch wrote: > same for me . havent grok everything yet. but thats a big steps between a > language, and an architecture..., This is a good point. Something I really like about Erlang is that it's simple enough to be understood completely. Moving to OTP *and* maintaining that "I know _exactly_ what's going on" feeling takes some work. For example, if you use a gen_server to write your first server, you'll get working code which is well-behaved. But nobody does that. Everyone writes a 'classic' receive-loop server first and thinks "hey! that's really neat and simple"*. And then you can go away and use gen_server because it makes a lot of fiddly details go away. But you never forget that the 'classic' server is the beautiful one. The same applies to (most of?) OTP. If you haven't solved the problem yourself in plain Erlang, it's hard to appreciate what the OTP solution is doing. But since we're all in a hurry and want to write something that works NOW, an OTP quick start book would be nice. All it takes is someone to write it... Matthias * Ok. Not everybody. Some people think "this is not complicated enough". Give them a DCOM developers kit. That'll make them happy. From matthias@REDACTED Tue Oct 10 11:13:05 2000 From: matthias@REDACTED (matthias@REDACTED) Date: Tue, 10 Oct 2000 11:13:05 +0200 (CEST) Subject: How to do line oriented i/o in constant space? In-Reply-To: References: Message-ID: <14818.56737.130162.816675@corelatus.com> Hi, I feel somewhat guilty for spreading (in the FAQ) the use of halt() in stand-along programs, e.g. Torbjorn Tornkvist writes: > loop(Port) -> > receive > {Port, eof} -> > halt() > end. > I'm about to put out a new release of the FAQ and was going to change all occurences of halt() to init:stop(). The latter is a graceful shutdown, as compared to halt's 'kill -9' behaviour. Before I do that, any comments? Matthias (previously known as matthias.lang@REDACTED) From luke@REDACTED Tue Oct 10 11:14:00 2000 From: luke@REDACTED (Luke Gorrie) Date: 10 Oct 2000 11:14:00 +0200 Subject: Suggested Example/ emacs questions In-Reply-To: matthias@corelatus.com's message of "Tue, 10 Oct 2000 10:55:24 +0200 (CEST)" References: <402DD461F109D411977E0008C791C3125651B8@imp02mbx.one2one.co.uk> <39E0E5A1.F9A0A5B5@europemail.com> <14818.55676.460593.617173@corelatus.com> Message-ID: matthias@REDACTED writes: > For example, if you use a gen_server to write your first server, > you'll get working code which is well-behaved. But nobody does > that. Everyone writes a 'classic' receive-loop server first and thinks > "hey! that's really neat and simple"*. And then you can go away and use > gen_server because it makes a lot of fiddly details go away. But > you never forget that the 'classic' server is the beautiful one. Yup. Lately I've been noticing that I don't write the receive loops of many servers, because I just use gen_server. That seems like a shame because what I really miss is the lovely "I communicate, therefore I synchronise" style you get if you use guarded receive clauses to decide in which order you'll receive messages, e.g.: receive {push, Item} when Full == false -> to implicitly block push requests for a bounded stack while it's full, and that sort of thing. The stuff I got excited about when reading _communicating sequential processes_. Anyone empathise? :-) I'm not sure whether it's just that most of my programs don't need guarded receives and fancy synchronisation, or whether I'm just too far into the habbit of using gen_server to write beautiful receive loops. I'm suspicious that it's the latter :) -Luke From etxuwig@REDACTED Tue Oct 10 11:22:46 2000 From: etxuwig@REDACTED (Ulf Wiger) Date: Tue, 10 Oct 2000 11:22:46 +0200 (MET DST) Subject: Suggested Example/ emacs questions In-Reply-To: Message-ID: On 10 Oct 2000, Luke Gorrie wrote: >Yup. Lately I've been noticing that I don't write the receive loops of >many servers, because I just use gen_server. That seems like a shame >because what I really miss is the lovely "I communicate, therefore I >synchronise" style you get if you use guarded receive clauses to >decide in which order you'll receive messages, e.g.: > > receive {push, Item} when Full == false -> > >to implicitly block push requests for a bounded stack while it's full, >and that sort of thing. The stuff I got excited about when reading >_communicating sequential processes_. > >Anyone empathise? :-) I'm not sure whether it's just that most of my >programs don't need guarded receives and fancy synchronisation, or >whether I'm just too far into the habbit of using gen_server to write >beautiful receive loops. I'm suspicious that it's the latter :) I certainly empathise. I, too, miss the good old days when I wrote my own receive statements. You can't really beat the vanilla Erlang FSM for beauty. What messes things up are those darn system messages, and trace and debug functionality. As far as trace and debug is concerned, OTP R7 should give us an opportunity to achieve much more elegant debugging. System messages are much harder. How to accomplish the suspend/ code_change/resume functionality, as well as the ordered shutdowns, without messing up the normal program? I've been wishing for some kind of "protected mode" section, where I can write hooks to modify message reception; system messages would then be somehow pre-empted by OTP, unless I explicitly state that I want to see them. ... or am I smoking something funny? /Uffe -- Ulf Wiger tfn: +46 8 719 81 95 Network Architecture & Product Strategies mob: +46 70 519 81 95 Ericsson Telecom AB, Datacom Networks and IP Services Varuv?gen 9, ?lvsj?, S-126 25 Stockholm, Sweden From luke@REDACTED Tue Oct 10 11:29:48 2000 From: luke@REDACTED (Luke Gorrie) Date: 10 Oct 2000 11:29:48 +0200 Subject: Suggested Example/ emacs questions In-Reply-To: Ulf Wiger's message of "Tue, 10 Oct 2000 11:22:46 +0200 (MET DST)" References: Message-ID: Ulf Wiger writes: > System messages are much harder. How to accomplish the suspend/ > code_change/resume functionality, as well as the ordered shutdowns, > without messing up the normal program? > > I've been wishing for some kind of "protected mode" section, where > I can write hooks to modify message reception; system messages > would then be somehow pre-empted by OTP, unless I explicitly state > that I want to see them. Another dubious possibility I was thinking of is inline'ing all the system message handling with a macro, like: receive pop when Empty == false -> ...; {push, Item} when Full == false -> ...; ?handle_system_messages end Just what that macro would expand to, I've no firm idea :-) Using non-trivial macros in Erlang doesn't feel good, though. -Luke From etxuwig@REDACTED Tue Oct 10 11:43:25 2000 From: etxuwig@REDACTED (Ulf Wiger) Date: Tue, 10 Oct 2000 11:43:25 +0200 (MET DST) Subject: system messages (Re: Suggested Example/ emacs questions) In-Reply-To: Message-ID: I've made several false-starts trying to improve the system messages situation. One idea has been to do a parse transform, looking for e.g. 'system_receive' instead of 'receive', and then inserting a match on system messages. It's not that hard to store needed information in the process dictionary, so that the necessary info can be extracted and passed to the sys:handle_system_msg/6 function. A problem then becomes what to do with a module that contains receive statements, but no system_receive. It might be appropriate to leave them alone. Another problem arises with the shutdown protocol. I'd like to also insert a match for {'EXIT', Parent, shutdown} in the system_receive, but I don't have Parent available... ... actually, I would, if I were allowed to use get and put in guards: receive {'EXIT', Parent, shutdown} when Parent == hd(get('$ancestors')) -> shutdown(...); ... end. Since get() and put() are O(1) nowadays, why can't I use them in guards? /Uffe On 10 Oct 2000, Luke Gorrie wrote: >Ulf Wiger writes: > >> System messages are much harder. How to accomplish the suspend/ >> code_change/resume functionality, as well as the ordered shutdowns, >> without messing up the normal program? >> >> I've been wishing for some kind of "protected mode" section, where >> I can write hooks to modify message reception; system messages >> would then be somehow pre-empted by OTP, unless I explicitly state >> that I want to see them. > >Another dubious possibility I was thinking of is inline'ing all the >system message handling with a macro, like: > > receive > pop when Empty == false -> ...; > {push, Item} when Full == false -> ...; > ?handle_system_messages > end > >Just what that macro would expand to, I've no firm idea :-) > >Using non-trivial macros in Erlang doesn't feel good, though. > >-Luke > > -- Ulf Wiger tfn: +46 8 719 81 95 Network Architecture & Product Strategies mob: +46 70 519 81 95 Ericsson Telecom AB, Datacom Networks and IP Services Varuv?gen 9, ?lvsj?, S-126 25 Stockholm, Sweden From tobbe@REDACTED Tue Oct 10 11:50:21 2000 From: tobbe@REDACTED (Torbjorn Tornkvist) Date: 10 Oct 2000 11:50:21 +0200 Subject: system messages (Re: Suggested Example/ emacs questions) In-Reply-To: Ulf Wiger's message of "Tue, 10 Oct 2000 11:43:25 +0200 (MET DST)" References: Message-ID: > Since get() and put() are O(1) nowadays, why can't I use them in > guards? But wouldn't that give us strange side-effects ? What happends if a put, is executed (left-to-right) but later (furter-to-the-right) the clause match fails ? Also, a clause may now match, not only based on its input arguments but alos on what get() may return, which I think is highly undesirable. Cheers /Tobbe From etxuwig@REDACTED Tue Oct 10 11:53:02 2000 From: etxuwig@REDACTED (Ulf Wiger) Date: Tue, 10 Oct 2000 11:53:02 +0200 (MET DST) Subject: system messages (Re: Suggested Example/ emacs questions) In-Reply-To: Message-ID: OK, I don't know why I included put() -- I don't want that either. get() has no side-effects, though. That's the one I want. /Uffe On 10 Oct 2000, Torbjorn Tornkvist wrote: > >> Since get() and put() are O(1) nowadays, why can't I use them in >> guards? > >But wouldn't that give us strange side-effects ? >What happends if a put, is executed (left-to-right) >but later (furter-to-the-right) the clause match fails ? >Also, a clause may now match, not only based on its input >arguments but alos on what get() may return, which I >think is highly undesirable. > >Cheers /Tobbe > -- Ulf Wiger tfn: +46 8 719 81 95 Network Architecture & Product Strategies mob: +46 70 519 81 95 Ericsson Telecom AB, Datacom Networks and IP Services Varuv?gen 9, ?lvsj?, S-126 25 Stockholm, Sweden From joe@REDACTED Tue Oct 10 11:57:38 2000 From: joe@REDACTED (Joe Armstrong) Date: Tue, 10 Oct 2000 11:57:38 +0200 (CEST) Subject: Suggested Example/ emacs questions In-Reply-To: Message-ID: On Tue, 10 Oct 2000, Ulf Wiger wrote: > On 10 Oct 2000, Luke Gorrie wrote: > > >Yup. Lately I've been noticing that I don't write the receive loops of > >many servers, because I just use gen_server. That seems like a shame > >because what I really miss is the lovely "I communicate, therefore I > >synchronise" style you get if you use guarded receive clauses to > >decide in which order you'll receive messages, e.g.: > > > > receive {push, Item} when Full == false -> > > > >to implicitly block push requests for a bounded stack while it's full, > >and that sort of thing. The stuff I got excited about when reading > >_communicating sequential processes_. > > > >Anyone empathise? :-) I'm not sure whether it's just that most of my > >programs don't need guarded receives and fancy synchronisation, or > >whether I'm just too far into the habbit of using gen_server to write > >beautiful receive loops. I'm suspicious that it's the latter :) > > > I certainly empathise. > > I, too, miss the good old days when I wrote my own receive > statements. You can't really beat the vanilla Erlang FSM for beauty. > What messes things up are those darn system messages, and trace > and debug functionality. The reason behind the gen_servers FSM's, worker-supervisor trees etc. was twofold 1) Get everybody programming the same way We observed that in big projects with lots of programmers different programmers used different ways of programming things (say client-server) - we didn't want there to be ten ways of programming client-server in one project. So we made the gen_server behaviour to do this - then all the programmers would use the same way to program client-server and it would be easy (the idea was) for them to understand each other's code. 2) Give novice programmers access to expert code There are several "gottcha's" in programming client-server/worker-supervisor (especaily if you want to do fancy stuff) - When a novice programmer tries to "roll-their-own" they can made some fairly serious errors (I've seen some examples posted here, Non tail-recursive code for example) - so the idea was to provide a generic framework where novices could plug in their own callback routines. The idea was "If you're an expert and know what you're doing then feel free to 'roll-your-own' otherwise use the appropriate generic code" - Of course you loose the feeling of "seat of pants" control if you use the generic methods but you gain convenience plus the fact that other people can understand your code. For small hacks I always "roll-my-own" - but for big systems programming I'd either use the generic-methods OR write a new generic method first and then use that. > > As far as trace and debug is concerned, OTP R7 should give us an > opportunity to achieve much more elegant debugging. > > System messages are much harder. How to accomplish the suspend/ > code_change/resume functionality, as well as the ordered shutdowns, > without messing up the normal program? > > I've been wishing for some kind of "protected mode" section, where > I can write hooks to modify message reception; system messages > would then be somehow pre-empted by OTP, unless I explicitly state > that I want to see them. > > ... or am I smoking something funny? Not in the slightest. OTP was designed under great time pressure - and was the 2'nd attempt at an architecture. It's roots were the BOS (Basic OS) of the ACS/Dunder project + diverse generic servers from the CS lab. It would be nice to do a 3'rd architecture and feed into it what was learn't since OTP. Certain things are very wrong and very deep :-( - I'd like to see a *strict* pre-defined module structure and a strict OS ring structure to start with - also a strict package structure. If you, for example, plot the intermodule call graph you get pure spagetti - A strict tree would be *much* nicer. The system should also have a stricter type structure - For example, file names can still be atoms, strings or deep strings - this despite the fact we tried to encourage people to use only flat strings. Some of ROK's ideas on "flotillas" should be implemented (export_to) etc. /Joe -- Joe Armstrong, Alteon WebSystems, tel: +46 8-545 550 00 S:t Eriksgatan 44, IV, fax: +46 8-545 550 50 SE-112 32 Stockholm, Sweden info: www.bluetail.com From rv@REDACTED Tue Oct 10 11:58:15 2000 From: rv@REDACTED (Robert Virding) Date: Tue, 10 Oct 2000 11:58:15 +0200 Subject: system messages (Re: Suggested Example/ emacs questions) In-Reply-To: Your message of "Tue, 10 Oct 2000 11:43:25 +0200." Message-ID: <200010100958.LAA30141@trana.bluetail.com> Ulf Wiger writes: > >Since get() and put() are O(1) nowadays, why can't I use them in >guards? > The main requirement for what can go in guards is not really that it is O(1) but that it must be side-effect free. Which put/2 is not. Personally I have always considered the process dictionary to be a BIG mistake and therefore feel we should do nothing which encourages it, like making it accessable in guards. :-) Robert -- Robert Virding Tel: +46 (0)8 545 55 017 Alteon Web Systems Email: rv@REDACTED S:t Eriksgatan 44 WWW: http://www.bluetail.com/~rv SE-112 32 Stockholm, SWEDEN "Folk s?ger att jag inte bryr mig om n?gonting, men det skiter jag i". From tobbe@REDACTED Tue Oct 10 12:07:13 2000 From: tobbe@REDACTED (Torbjorn Tornkvist) Date: 10 Oct 2000 12:07:13 +0200 Subject: system messages (Re: Suggested Example/ emacs questions) In-Reply-To: Ulf Wiger's message of "Tue, 10 Oct 2000 11:53:02 +0200 (MET DST)" References: Message-ID: > OK, I don't know why I included put() -- I don't want that either. > get() has no side-effects, though. That's the one I want. But get() is not good either since it breaks the simple idea of a pure function, i.e: 1. You can only affect the behaviour of a function by what arguments you are sending into it. 2. A function may only affect its surrounding by what output it produces. 3. The same input arguments should always produce the same output value. So get() breaks both 1) and 3) and I think we already got enough of side-effects in the language as it is today ;-) Cheers /Tobbe From etxuwig@REDACTED Tue Oct 10 13:00:53 2000 From: etxuwig@REDACTED (Ulf Wiger) Date: Tue, 10 Oct 2000 13:00:53 +0200 (MET DST) Subject: system messages (Re: Suggested Example/ emacs questions) In-Reply-To: <200010100958.LAA30141@trana.bluetail.com> Message-ID: On Tue, 10 Oct 2000, Robert Virding wrote: >Ulf Wiger writes: >> >>Since get() and put() are O(1) nowadays, why can't I use them in >>guards? >> > >The main requirement for what can go in guards is not really that it is >O(1) but that it must be side-effect free. Which put/2 is not. Yes, forget put() in guards. >Personally I have always considered the process dictionary to be a >BIG mistake and therefore feel we should do nothing which encourages >it, like making it accessable in guards. :-) Personally, I think that the process dictionary is no worse than using a named protected ets table (which has almost the same characteristics). Actually, the process dictionary is better in the sense that you can see its contents in crash reports and using process_info/1 or sys:get_status/1. The fact that the process dictionary *is* used for some pretty fundamental stuff (like proc_lib) indicates that it fills a gap. BTW, for my original purpose, there would be other ways to solve it: process_info(self(), parent), for example (but this isn't allowed in guards either). Why wouldn't there be a way for me to use information like Parent, RegisteredName, MessageQueueLen, ProcessPriority, in guards without carrying them around as arguments or variables? This is one important reason why a lot of manual work is required for writing your own OTP behaviours: you can't write an efficient support library without manual work, and it's even harder if you're not allowed to use e.g. the process dictionary. /Uffe -- Ulf Wiger tfn: +46 8 719 81 95 Network Architecture & Product Strategies mob: +46 70 519 81 95 Ericsson Telecom AB, Datacom Networks and IP Services Varuv?gen 9, ?lvsj?, S-126 25 Stockholm, Sweden From daniel.neri@REDACTED Tue Oct 10 13:06:12 2000 From: daniel.neri@REDACTED (Daniel Neri) Date: 10 Oct 2000 13:06:12 +0200 Subject: OTP R7B minor issues during build Message-ID: Hi, I just put R7B on a laptop running NetBSD 1.4.1, and there were two small problems. 1. As previously mentioned on this list[1], NetBSD doesn't define IPPORT_USERRESERVED, so I used IPPORT_RESERVED instead. The offending NetBSD header file can be found at [2]. I've attached a trivial fix below. 2. $ERL_TOP/lib/erl_interface/src/portability.h doesn't live up to its (file)name. Basically, it assumes that GCC version >= 2 means that the ELF object file format is used, which is untrue for both OpenBSD and NetBSD (and I guess also non-recent FreeBSD releases). Maybe the configure script should check for ELF somehow... Otherwise, everything seems to work fine. Best wishes, --Daniel [1] http://www.erlang.org/ml-archive/erlang-questions/200008/msg00113.html [2] http://cvsweb.netbsd.org/bsdweb.cgi/syssrc/sys/netinet/in.h?rev=1.51&content-type=text/x-cvsweb-markup -------------- next part -------------- A non-text attachment was scrubbed... Name: not available Type: text/x-patch Size: 635 bytes Desc: OTP R7B NetBSD fix URL: -------------- next part -------------- -- Daniel Neri mailto:dn@REDACTED Sigicom AB, Sweden http://www.sigicom.com From etxuwig@REDACTED Tue Oct 10 13:08:12 2000 From: etxuwig@REDACTED (Ulf Wiger) Date: Tue, 10 Oct 2000 13:08:12 +0200 (MET DST) Subject: Suggested Example/ emacs questions In-Reply-To: Message-ID: On Tue, 10 Oct 2000, Joe Armstrong wrote: > The reason behind the gen_servers FSM's, worker-supervisor trees >etc. was twofold > > 1) Get everybody programming the same way This is a Good Thing. > 2) Give novice programmers access to expert code > The idea was "If you're an expert and know what you're doing then >feel free to 'roll-your-own' otherwise use the appropriate generic code" - >Of course you loose the feeling of "seat of pants" control if you use the >generic methods but you gain convenience plus the fact that other people >can understand your code. > > For small hacks I always "roll-my-own" - but for big systems >programming I'd either use the generic-methods OR write a new generic >method first and then use that. My contention is that it's too hard to roll your own using today's support. The behaviours are great, for the above reasons, but you really have to know what you're doing to try to make your own behaviour or bare-bones implementation in a production system. We have a few processes in our system called "classic servers", in places where it made sense to optimize away the gen_server overhead. After a while, we ended up going back to gen_server and paying the extra overhead, because rolling your own was simply too error prone. There was too much code that needed to be written by hand -- not necessarily because it did anything unusual, but because it fell just a fraction short of being possible to generalize. At least 90% of it is copy and paste. The remaining 10% causes the headaches. I'm certain the situation could be improved, and that we should continue to try and find better solutions. /Uffe -- Ulf Wiger tfn: +46 8 719 81 95 Network Architecture & Product Strategies mob: +46 70 519 81 95 Ericsson Telecom AB, Datacom Networks and IP Services Varuv?gen 9, ?lvsj?, S-126 25 Stockholm, Sweden From tony@REDACTED Tue Oct 10 13:19:46 2000 From: tony@REDACTED (Tony Rogvall) Date: Tue, 10 Oct 2000 13:19:46 +0200 Subject: Suggested Example/ emacs questions References: Message-ID: <39E2FB52.CB8FE6E9@bluetail.com> Ulf Wiger wrote: > > > As far as trace and debug is concerned, OTP R7 should give us an > opportunity to achieve much more elegant debugging. > > System messages are much harder. How to accomplish the suspend/ > code_change/resume functionality, as well as the ordered shutdowns, > without messing up the normal program? > > I've been wishing for some kind of "protected mode" section, where > I can write hooks to modify message reception; system messages > would then be somehow pre-empted by OTP, unless I explicitly state > that I want to see them. > > ... or am I smoking something funny? Who am I to judge :-) Me and pekka designed a multithreaded implementation of erlang ~ two years ago (put on ice). Then we remodel the message passing and signal sending so that a message was a sort of a signal. i.e. everything was signals. It was then up to the receiving process (internally) to sort things out. One thing that I thought about then (or was it pekka) was to implement a per process signal handler, similar to the error handler. Then this sorting out messages from signals / exit propagation / code reloading / suspend / resume could be implemented in erlang in a signal handler. What do you think about that one ??? Or am I on heavy drugs now ? /Tony -------------- next part -------------- A non-text attachment was scrubbed... Name: tony.vcf Type: text/x-vcard Size: 328 bytes Desc: Card for Tony Rogvall URL: From etxuwig@REDACTED Tue Oct 10 13:27:51 2000 From: etxuwig@REDACTED (Ulf Wiger) Date: Tue, 10 Oct 2000 13:27:51 +0200 (MET DST) Subject: Suggested Example/ emacs questions In-Reply-To: <39E2FB52.CB8FE6E9@bluetail.com> Message-ID: On Tue, 10 Oct 2000, Tony Rogvall wrote: >One thing that I thought about then (or was it pekka) was to implement a >per process signal handler, similar to the error handler. Then this >sorting out messages from signals / exit propagation / code reloading / >suspend / resume could be implemented in erlang in a signal handler. > >What do you think about that one ??? Cool! /Uffe -- Ulf Wiger tfn: +46 8 719 81 95 Network Architecture & Product Strategies mob: +46 70 519 81 95 Ericsson Telecom AB, Datacom Networks and IP Services Varuv?gen 9, ?lvsj?, S-126 25 Stockholm, Sweden From Sean.Hinde@REDACTED Tue Oct 10 16:55:03 2000 From: Sean.Hinde@REDACTED (Sean Hinde) Date: Tue, 10 Oct 2000 15:55:03 +0100 Subject: Suggested Example/ emacs questions Message-ID: <402DD461F109D411977E0008C791C3125651C4@imp02mbx.one2one.co.uk> > On Tue, 10 Oct 2000, Tony Rogvall wrote: > > >One thing that I thought about then (or was it pekka) was to > implement a > >per process signal handler, similar to the error handler. Then this > >sorting out messages from signals / exit propagation / code > reloading / > >suspend / resume could be implemented in erlang in a signal handler. > > > >What do you think about that one ??? > > Cool! It would also be nice to have some clever overload handling mechanism to monitor queue lengths and then maybe have some types of messages discarded and others pushed to the front.. (if that makes any sense in a system which does selective receive?) We just got this sort of thing in AXE10 for c7 TCAP signalling. There are different priority queues and it will discard messages from lower priority queues according to some clever alogorithm which achieves maximum throughput under extreme provocation without filling up the various buffers and falling over. Sean NOTICE AND DISCLAIMER: This email (including attachments) is confidential. If you have received this email in error please notify the sender immediately and delete this email from your system without copying or disseminating it or placing any reliance upon its contents. We cannot accept liability for any breaches of confidence arising through use of email. Any opinions expressed in this email (including attachments) are those of the author and do not necessarily reflect our opinions. We will not accept responsibility for any commitments made by our employees outside the scope of our business. We do not warrant the accuracy or completeness of such information. From Sean.Hinde@REDACTED Tue Oct 10 17:18:00 2000 From: Sean.Hinde@REDACTED (Sean Hinde) Date: Tue, 10 Oct 2000 16:18:00 +0100 Subject: compiler available for x86 ?. Message-ID: <402DD461F109D411977E0008C791C3125651C6@imp02mbx.one2one.co.uk> There is always Stand Alone Erlang which has had experimental support for some time. I've never played with it but there is something in $ROOT/erts/sae Maybe the OTP team are planning to turn this into supported code sometime :-) Sean -----Original Message----- From: Luc Taesch [mailto:ltaesch@REDACTED] Sent: 8 October 2000 21:52 To: Sean Hinde Cc: erlang-questions@REDACTED Subject: Re: compiler available for x86 ?. Sean Hinde wrote: I guess you are talking about Linux, in which case the normal R7B etc release works fine. It doesn't do native code compilation but beam compiled code is fast enough for most things. Maybe I missed the point of your question? sorry for being so imprecise. yes, im interesting in distributing a binary only. for convenience. say 1) i wanna deploy 30 times the same application, on client servers, and i dont wanna bother intalling erlang, or 2) i have access to a server i cant impose to have erlang installed (say sourceforge) 3) or i wanna build utilitaries, and i dont wanna be troubled with release conflicts as time goes by. 4) a demo softwarein a rpm, and i dont want bothering people downloading 8 mo of environement, or they cant be root ( to install erlang) these are different points where i may be interested to build a self contained binary. so i was looking at how to do that. and which constraints it may brings, like language restrictions. (and yes, R7b is quick enough for me!) Sean > -----Original Message----- > From: Luc Taesch [ mailto:ltaesch@REDACTED ] > Sent: 7 October 2000 12:35 > To: erlang-questions@REDACTED > Subject: compiler available for x86 ?. > > > i once asked which of compiler is available for x86. > hipe is for sparc, and etos link is still un-available. > > -- First, they ignore you. Then, they laugh at you. Then, they fight you. Then, you win. --- Gandhi. Working code is what matter, not your market capitalization. --Kurt granroth NOTICE AND DISCLAIMER: This email (including attachments) is confidential. If you have received this email in error please notify the sender immediately and delete this email from your system without copying or disseminating it or placing any reliance upon its contents. We cannot accept liability for any breaches of confidence arising through use of email. Any opinions expressed in this email (including attachments) are those of the author and do not necessarily reflect our opinions. We will not accept responsibility for any commitments made by our employees outside the scope of our business. We do not warrant the accuracy or completeness of such information. From Sean.Hinde@REDACTED Tue Oct 10 18:55:42 2000 From: Sean.Hinde@REDACTED (Sean Hinde) Date: Tue, 10 Oct 2000 17:55:42 +0100 Subject: release handling Message-ID: <402DD461F109D411977E0008C791C3125651CB@imp02mbx.one2one.co.uk> Following on from the earlier debate about losing understanding of what the system is doing when using behaviours I've started to look at the release handler. I just want to upgrade the SNMP app to the latest version. According to the official procedure you need to create a whole new release of everything, relup and appup files and use more complex scripts to do the upgrade. I wanted to be able to do this so that I knew what was happening... First I hacked systools:make_tar/1 to get it to make a tar file just containing the new SNMP version ignoring all the dependencies. I created a new start.boot by amending my existing one (created initially using systools:make_script/1) and recompiling using script2boot/1. We created a new directory underneath releases with the new release name, and changed the start_erl.data file to point to my new version. We then did init:restart(). but got the old version back again ;-) init:stop() followed by start uses the new version.. My question is really is there a way to make the system use the new version on init:restart(). without going through the entire upgrade process as prescribed in the manuals? Alternatively, has anyone found a simple mechanism to upgrade a single App without going through all the heartache of appup files etc? Rgds, Sean NOTICE AND DISCLAIMER: This email (including attachments) is confidential. If you have received this email in error please notify the sender immediately and delete this email from your system without copying or disseminating it or placing any reliance upon its contents. We cannot accept liability for any breaches of confidence arising through use of email. Any opinions expressed in this email (including attachments) are those of the author and do not necessarily reflect our opinions. We will not accept responsibility for any commitments made by our employees outside the scope of our business. We do not warrant the accuracy or completeness of such information. From vances@REDACTED Tue Oct 10 23:27:56 2000 From: vances@REDACTED (Vance Shipley) Date: Tue, 10 Oct 2000 17:27:56 -0400 Subject: release handling In-Reply-To: <402DD461F109D411977E0008C791C3125651CB@imp02mbx.one2one.co.uk> Message-ID: When we packaged our first application for embedded use we followed the instructions and examples as closely as possible. We had some difficulty but luckily at that time we had a visitor from the Erlang group here and he helped us through it. What suprised us was that the release_handler did not allow us to build an initial system. It must have a release upgrade specification file to complete the job and there doesn't seem to be a valid syntax for "upgrading" from nothing. I was also somewhat suprised that it packaged all the modules which were dependent including kernel and stdlib. This makes the standard release file much larger than needed. I may change that one day but we generally stick to the supported path as much as possible because we create enough of our own problems. :) My current steps to build and install a release are (assuming that the we have written a Beta.rel "release resource file" and an sample.app "application resource file"): $ make gcc -O -DUNIX -I. -I/usr/include/dialogic \ -I/usr/local/lib/erlang/lib/erl_interface-3.2/include \ -o dialogic dialogic.c -lgc -ldxxx -ldti -lsrl -lLiS \ -L/usr/local/lib/erlang/lib/erl_interface-3.2/lib \ -lerl_interface -lei -lresolv erlc sample.erl erlc sample_sup.erl erlc t1_fsm.erl erlc e1_fsm.erl erlc evasrv.erl erlc spanfsm.erl erlc cdrevh.erl erlc logevh.erl erlc evmgr.erl erlc status.erl erlc gentabs.erl $ make install install -d /usr/local/lib/erlang/lib/sample-2.0/ebin install -d /usr/local/lib/erlang/lib/sample-2.0/include install -d /usr/local/lib/erlang/lib/sample-2.0/src/c_src install -d /usr/local/lib/erlang/lib/sample-2.0/src/e_src install -d /usr/local/lib/erlang/lib/sample-2.0/priv/bin install -d /usr/local/lib/erlang/lib/sample-2.0/priv/data install Makefile /usr/local/lib/erlang/lib/sample-2.0/src/c_src install *.rel /usr/local/lib/erlang/releases install *.app /usr/local/lib/erlang/lib/sample-2.0/ebin install *.hrl /usr/local/lib/erlang/lib/sample-2.0/include install *.erl /usr/local/lib/erlang/lib/sample-2.0/src/e_src install *.beam /usr/local/lib/erlang/lib/sample-2.0/ebin install *.c /usr/local/lib/erlang/lib/sample-2.0/src/c_src install -s dialogic /usr/local/lib/erlang/lib/sample-2.0/priv/bin install *.c /usr/local/lib/erlang/lib/sample-2.0/src/c_src install *.defs /usr/local/lib/erlang/lib/sample-2.0/priv/data install *tab /usr/local/lib/erlang/lib/sample-2.0/priv/data $ cd /usr/local/lib/erlang/releases $ erl Erlang (BEAM) emulator version 4.9.1 [source] Eshell V4.9.1 (abort with ^G) 1> systools:make_script("Beta"). ok 2> systools:make_tar("Beta"). ok 3> halt(). $ cp Beta.tar.gz /home/otpuser/lib/erlang/releases $ exit login: otpuser Password: $ cd lib/erlang/releases $ erl Erlang (BEAM) emulator version 4.9.1 [source] Eshell V4.9.1 (abort with ^G) 1> release_handler:unpack_release("Beta"). ok 2> halt(). $ vi RELEASES [{release,"sample", "example-1.0", "4.9.1", [{kernel,"2.5","/home/otpuser/lib/erlang/lib/kernel-2.5"}, {stdlib,"1.8.1","/home/otpuser/lib/erlang/lib/stdlib-1.8.1"}, {sasl,"1.8.2","/home/otpuser/lib/erlang/lib/sasl-1.8.2"}, {answer,"2.0","/home/otpuser/lib/erlang/lib/sample-2.0"}], unpacked}, {release,"OTP APN 181 01", "R6B", "4.9.1", [{kernel,"2.5","/home/otpuser/lib/erlang/lib/kernel-2.5"}, {stdlib,"1.8.1","/home/otpuser/lib/erlang/lib/stdlib-1.8.1"}, {sasl,"1.8.2","/home/otpuser/lib/erlang/lib/sasl-1.8.2"}], old}]. *** [ here we change the status of the new release from "unpacked" to "current"] *** $ erl Erlang (BEAM) emulator version 4.9.1 [source] Eshell V4.9.1 (abort with ^G) 1> release_handler:make_permanent("example-1.0"). ok 2> halt() At this point all that is needed to get an embedded system up and running is to run /home/otpuser/lib/erlang/bin/start. Now if the truth be known I usually skip the release_handler:unpack_release/1 step and just untar it manually. As far as I know that's all it really does. One day I'll have my makefile do all of this. :) -Vance From stimuli@REDACTED Wed Oct 11 03:45:10 2000 From: stimuli@REDACTED (Jeffrey Straszhiem) Date: Tue, 10 Oct 2000 21:45:10 -0400 Subject: Suggested Example In-Reply-To: <39E289D4.5F49DB77@bellsouth.net> References: <39E289D4.5F49DB77@bellsouth.net> Message-ID: <20001010214510.A1268@bzzt.shadow.net> On Mon, Oct 09, 2000 at 11:15:32PM -0400, Steve Elkins wrote: > Summing up, dshb is a very skeletal app, cosb has a little meat, and > udp_spc is the forerunner of cosb. Supplemental reading would be > chapters 5, 7, and 8 of Design Principles, as well as the relevant > man pages. And sys:trace/2 is wonderful. Thanks, these examples were very helpful. I even got the funky sys:trace thing to work :) Which, by the way, says a lot for Erlang with regards to ease of learning. I've been playing with it for a couple of weeks now, and not only do I understand a simple example like this, I've been crawling through the library code for the behaviors and, while I certainly don't get all of the detail, I basically get what is going on. It helps that I've spend the last six months playing with Haskell (believe it or not I understand monads :), so the FP paradigm is clear to me, but when the internal library code is relatively straightforward is a great thing -- of course what I should say is when the internal library code *can* be relatively straightforward is a great thing. Erlang is truly easy to learn. That being said, I still can't quite make sense out of the lower levels of I/O. That is the difference between the io module and Ports. And what the heck is a process group? :) -- Jeffrey Straszheim | A sufficiently advanced -- Systems Engineer, Programmer | regular expression is -- http://www.shadow.net/~stimuli | indistinguishable from -- stimuli AT shadow DOT net | magic From klacke@REDACTED Wed Oct 11 10:13:33 2000 From: klacke@REDACTED (Klacke) Date: Wed, 11 Oct 2000 10:13:33 +0200 Subject: How to do line oriented i/o in constant space? In-Reply-To: <402DD461F109D411977E0008C791C3125651BB@imp02mbx.one2one.co.uk> References: <402DD461F109D411977E0008C791C3125651BB@imp02mbx.one2one.co.uk> Message-ID: <20001011101333.A25807@bluetail.com> On Mon, Oct 09, 2000 at 03:39:19PM +0100, Sean Hinde wrote: > > > A middle way would be > > > to get binaries and then convert to lists as required. > > > > > > > > > Possibly. > > So what would be the efficient way to do this? > > Sean It would be to open a Port to the io channel, receive binary messages from the Port, and then either use the Bitsyntax or appropriate binary_to_lists's to pull the binary object apart. This is how we typically do socket and file programming in erlang today. A similar approach is indeed possible with stdin/stdout using the open_port({fd, 0, 1 .... approach. So the io: routines are nice if a high degree of flexibility is needed and the performance requirements are low, whereas the direct port mechanism is better if performance is important. A nice example where a high degree of flexibility is needed is the erlang shell. This was also the first application which used the io: routines and ... the io: routines were designed to cope with the requirements of the shell. /klacke -- Claes Wikstrom Bluetail AB http://www.bluetail.com From siri.hansen@REDACTED Wed Oct 11 11:02:17 2000 From: siri.hansen@REDACTED (Siri Hansen) Date: Wed, 11 Oct 2000 10:02:17 +0100 Subject: release handling References: Message-ID: <39E42C99.14AC82DC@eei.ericsson.se> Hi - I've been working with software upgrade for a while, and I've prototyped a few releases for the purpose. Later, my project decided to go for a simpler (not hot-swap) solution, but anyway - The OTP hot-swap is actually quite cool - if not too well tested! Vance Shipley wrote: > What suprised us was that the release_handler did not > allow us to build an initial system. It must have a > release upgrade specification file to complete the job > and there doesn't seem to be a valid syntax for > "upgrading" from nothing. Wouldn't this syntax of the relup file be the same as upgrading from nothing? {"1.0",[],[]}. This is what you get if you do systools:make_relup/4,and there are no .appup files in the given path. > I was also somewhat suprised that it packaged all the > modules which were dependent including kernel and stdlib. > This makes the standard release file much larger than > needed. I may change that one day but we generally stick > to the supported path as much as possible because we > create enough of our own problems. :) All applications mentioned in the .rel file will be packed with systools:make_tar/2, and the reason is that it should be opssible to sell the release as a complete system - i.e. the customer does not have to have Erlang/OTP installed before he can use the system. > Now if the truth be known I usually skip the release_handler:unpack_release/1 > step and just untar it manually. As far as I know that's all it really does. Does the RELEASES file look the same if you untar the release manually? I think that release_handler:unpack_release/1 also serves the purpose of informing the release handler about the new release. If you untar the release manually, you should also use release_handler:set_unpacked/2. Sean Hinde wrote: > Alternatively, has anyone found a simple mechanism to upgrade a single App > without going through all the heartache of appup files etc? As for using init:restart() for upgrade, I don't know to much about it. It seems like init:restart() uses exactly the same boot arguments as given when the node was started in the first place. However, if you would like to try the release handler anyway - given the SNMP application is delivered by OTP, the snmp.appup file allready exists, so you only have to create the relup file (systools:make_relup/4). When doing the upgrade itself, do release_handler:unpack_release/1 (or untar manually and do release_handler:set_unpacked/2), then release_handler:install_release/1 and release_handler:make_permanent/1. /siri From etxuwig@REDACTED Wed Oct 11 12:04:18 2000 From: etxuwig@REDACTED (Ulf Wiger) Date: Wed, 11 Oct 2000 12:04:18 +0200 (MET DST) Subject: release handling In-Reply-To: <402DD461F109D411977E0008C791C3125651CB@imp02mbx.one2one.co.uk> Message-ID: On Tue, 10 Oct 2000, Sean Hinde wrote: >We created a new directory underneath releases with the new release name, >and changed the start_erl.data file to point to my new version. > >We then did init:restart(). but got the old version back again ;-) I don't think init:restart() actually restarts the VM process; it's more of an internal reset. >init:stop() followed by start uses the new version.. You should be able to use init:reboot(), which basically does the above. >My question is really is there a way to make the system use the new version >on init:restart(). without going through the entire upgrade process as >prescribed in the manuals? The way OTP figures out which release to use is by reading the start_erl.data file. If you modify start_erl.data, and then call init:reboot(), it should work.... >Alternatively, has anyone found a simple mechanism to upgrade a single App >without going through all the heartache of appup files etc? Yes, AXD 301 has support for generating .rel and .appup files by parsing the .app files. We also perform some magic on the .relup file for some upgrade scenarios. /Uffe -- Ulf Wiger tfn: +46 8 719 81 95 Network Architecture & Product Strategies mob: +46 70 519 81 95 Ericsson Telecom AB, Datacom Networks and IP Services Varuv?gen 9, ?lvsj?, S-126 25 Stockholm, Sweden From siri.hansen@REDACTED Wed Oct 11 12:55:13 2000 From: siri.hansen@REDACTED (Siri Hansen) Date: Wed, 11 Oct 2000 11:55:13 +0100 Subject: release handling References: Message-ID: <39E44711.A3A6FC13@eei.ericsson.se> Ulf Wiger wrote: > > Yes, AXD 301 has support for generating .rel and .appup files by > parsing the .app files. I'd be interested in that! Would you let us have a look? /siri From etxuwig@REDACTED Wed Oct 11 12:57:48 2000 From: etxuwig@REDACTED (Ulf Wiger) Date: Wed, 11 Oct 2000 12:57:48 +0200 (MET DST) Subject: release handling In-Reply-To: <39E44711.A3A6FC13@eei.ericsson.se> Message-ID: On Wed, 11 Oct 2000, Siri Hansen wrote: >Ulf Wiger wrote: >> >> Yes, AXD 301 has support for generating .rel and .appup files by >> parsing the .app files. > >I'd be interested in that! Would you let us have a look? Hmm, yes. If you're asking as an Ericsson person, it's pretty easy. The AXD 301 source code is available on the internal Ericsson Web. I can guide you to the right modules and documentation. If you're asking as a general Erlang user, I will probably get around to publishing parts of our install/upgrade support, but I can tell you when that will be. /Uffe -- Ulf Wiger tfn: +46 8 719 81 95 Network Architecture & Product Strategies mob: +46 70 519 81 95 Ericsson Telecom AB, Datacom Networks and IP Services Varuv?gen 9, ?lvsj?, S-126 25 Stockholm, Sweden From luc.taesch@REDACTED Wed Oct 11 14:32:03 2000 From: luc.taesch@REDACTED (luc.taesch@REDACTED) Date: Wed, 11 Oct 2000 14:32:03 +0200 Subject: stylesheets Message-ID: i will soon produce some document, automatically from my systems , which is docbook/jade based. i would like to spare as little time as possible on the style sshet issue, and when reading the printed documentation from erlang, I had appreciated the style used (compact, clear, no too obfuscated). would u mind me looking at the stylesheets used for the docs ?(im assuming u use jade too)? (on top, i would be able to produce documentation for the oss system im doing on my own with an erlang-ish look) From Sean.Hinde@REDACTED Wed Oct 11 15:37:38 2000 From: Sean.Hinde@REDACTED (Sean Hinde) Date: Wed, 11 Oct 2000 14:37:38 +0100 Subject: release handling Message-ID: <402DD461F109D411977E0008C791C3125651D3@imp02mbx.one2one.co.uk> > Sean Hinde wrote: > > First I hacked systools:make_tar/1 to get it to make a tar file just > > containing the new SNMP version ignoring all the dependencies. Martin: > I think it would be great with an option to exclude all unchanged > apps. If you have added such an option, and you'd like to share it, I > think it should be included in the next OTP release. It wasn't nearly so nice - I just copied systools.erl and mangled it by deleting enough that it ignored enough dependency checks and extraneous stuff that it just provided the application in a tar file. I agree it would be very nice to have an option to ignore application dependencies. I'll try to find some time to analyse the havoc I caused to see if it can be made into a patch... It also doesn't look too hard to come up with an init:restart(Rel_name). which makes the new release permanent after some minimal checking that the files are there before restarting. It's a bit scary playing about with init though!! Sean NOTICE AND DISCLAIMER: This email (including attachments) is confidential. If you have received this email in error please notify the sender immediately and delete this email from your system without copying or disseminating it or placing any reliance upon its contents. We cannot accept liability for any breaches of confidence arising through use of email. Any opinions expressed in this email (including attachments) are those of the author and do not necessarily reflect our opinions. We will not accept responsibility for any commitments made by our employees outside the scope of our business. We do not warrant the accuracy or completeness of such information. From etxuwig@REDACTED Wed Oct 11 15:45:27 2000 From: etxuwig@REDACTED (Ulf Wiger) Date: Wed, 11 Oct 2000 15:45:27 +0200 (MET DST) Subject: release handling In-Reply-To: <402DD461F109D411977E0008C791C3125651D3@imp02mbx.one2one.co.uk> Message-ID: On Wed, 11 Oct 2000, Sean Hinde wrote: >It wasn't nearly so nice - I just copied systools.erl and mangled it >by deleting enough that it ignored enough dependency checks and >extraneous stuff that it just provided the application in a tar >file. I agree it would be very nice to have an option to ignore >application dependencies. I'll try to find some time to analyse the >havoc I caused to see if it can be made into a patch... For a "non-programmer", you're getting into pretty deep waters... (: /Uffe -- Ulf Wiger tfn: +46 8 719 81 95 Network Architecture & Product Strategies mob: +46 70 519 81 95 Ericsson Telecom AB, Datacom Networks and IP Services Varuv?gen 9, ?lvsj?, S-126 25 Stockholm, Sweden From Sean.Hinde@REDACTED Wed Oct 11 15:51:47 2000 From: Sean.Hinde@REDACTED (Sean Hinde) Date: Wed, 11 Oct 2000 14:51:47 +0100 Subject: release handling Message-ID: <402DD461F109D411977E0008C791C3125651D4@imp02mbx.one2one.co.uk> > For a "non-programmer", you're getting into pretty deep waters... > > (: I agree, and I don't want to be there. I felt even less confident though about handing over my system to the release handler. At least the tar file I produced at the bottom of my ocean could be very easily checked to be correct and hold the right files. I would have just tar'd up the directory but thought it would be interesting to try another way which gave me only the files in the .app file. I guess after two years I'll probably now have to admit to being some kind of an Erlang Programmer ;) Though definitely not any other kind!! BR, Sean NOTICE AND DISCLAIMER: This email (including attachments) is confidential. If you have received this email in error please notify the sender immediately and delete this email from your system without copying or disseminating it or placing any reliance upon its contents. We cannot accept liability for any breaches of confidence arising through use of email. Any opinions expressed in this email (including attachments) are those of the author and do not necessarily reflect our opinions. We will not accept responsibility for any commitments made by our employees outside the scope of our business. We do not warrant the accuracy or completeness of such information. From kent@REDACTED Wed Oct 11 15:56:47 2000 From: kent@REDACTED (Kent Boortz) Date: 11 Oct 2000 15:56:47 +0200 Subject: stylesheets In-Reply-To: luc.taesch@ubs.com's message of "Wed, 11 Oct 2000 14:32:03 +0200" References: Message-ID: > would u mind me looking at the stylesheets used for the docs ?(im > assuming u use jade too)? No, we use our own SGML to LaTeX/HTML/roff tool written in Erlang (DocBuilder). It is currently not Open Source. kent From mbj@REDACTED Wed Oct 11 10:32:11 2000 From: mbj@REDACTED (Martin Bjorklund) Date: Wed, 11 Oct 2000 10:32:11 +0200 Subject: release handling In-Reply-To: Your message of "Tue, 10 Oct 2000 17:55:42 +0100" <402DD461F109D411977E0008C791C3125651CB@imp02mbx.one2one.co.uk> References: <402DD461F109D411977E0008C791C3125651CB@imp02mbx.one2one.co.uk> Message-ID: <20001011103211I.mbj@bluetail.com> Sean Hinde wrote: > First I hacked systools:make_tar/1 to get it to make a tar file just > containing the new SNMP version ignoring all the dependencies. Background: systools:make_tar/1 creates a new release package, including the 'relup' file, emulator (optional). This package can be used as an initial release, or to upgrade a running system. However, it always contains all applications, even the apps which are not changed. Thus, the package is unnecessry large for small upgrades. I think it would be great with an option to exclude all unchanged apps. If you have added such an option, and you'd like to share it, I think it should be included in the next OTP release. At bluetail, we use make_tar to create the (large) package, then we have a script which unpacks the tar file, removes the unchanged apps, and packs everything together again. (BTW, this has to be done in erlang, since the package will be unpacked with erl_tar, which can't handle tar files created with the std tar in linux :( ) > We created a new directory underneath releases with the new release name, > and changed the start_erl.data file to point to my new version. > > We then did init:restart(). but got the old version back again ;-) Yes. init:restart() keeps the unix process, but restarts all erlang code. Thus, you'll get the same version. > init:stop() followed by start uses the new version.. > > My question is really is there a way to make the system use the new version > on init:restart(). without going through the entire upgrade process as > prescribed in the manuals? If you're using heart, do a init:reboot() instead. It starts a new unix process, and if you're using the start_erl scripts, you'll start the system from the new start.boot file. > Alternatively, has anyone found a simple mechanism to upgrade a single App > without going through all the heartache of appup files etc? You don't have to use the release_handler at all, if you don't want to. You would have to unpack the tar file yourself, make sure you've got the new start.boot script, update the start_erl.data file, and do a init:reboot(). In this case you'll loose the possibility to automatically reboot from the old release, should the new one crash, i.e. the new release will be permanent immediately. Vance Shipley describes a manual way (see my comments below) which uses the release_handler to unpack the release and make it permanent. "Vance Shipley" wrote: > What suprised us was that the release_handler did not > allow us to build an initial system. It must have a > release upgrade specification file to complete the job > and there doesn't seem to be a valid syntax for > "upgrading" from nothing. I haven't tested it, but I think that make_tar doesn't check the relup file, so you could include an empty relup for the initial release. > My current steps to build and install a release are (assuming > that the we have written a Beta.rel "release resource file" > and an sample.app "application resource file"): [stuff deleted] > $ vi RELEASES > *** [ here we change the status of the new release from "unpacked" > *** to "current"] At bluetail, we've found this necessary as well. I've patched the release_handler to do this. Actually, the release_handler could be made much more flexible. When it was designed we made it pretty "static" because we simply didn't know how it would be used in the field. As we gain experience, I think it should be opened up. Also, it was designed to be one component of a larger software management subsystem. We didn't think (and I still don't think) that a single app could/should be generic enough to do everything, e.g. downloading relase packages to all nodes in the distributed system, handle C-code, 3:rd party code, user interface etc - it all depends on the requirements of the application. I've added a few functions to the release_handler (see the enclosed file) which let you control it's behaviour better from such a software management subsystem. If more functions are needed, please add them and share with the list. Hopefully they'll make it into OTP... /martin -------------- next part -------------- %% ``The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in %% compliance with the License. You should have received a copy of the %% Erlang Public License along with this software. If not, it can be %% retrieved via the world wide web at http://www.erlang.org/. %% %% Software distributed under the License is distributed on an "AS IS" %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See %% the License for the specific language governing rights and limitations %% under the License. %% %% The Initial Developer of the Original Code is Ericsson Utvecklings AB. %% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings %% AB. All Rights Reserved.'' %% %% $Id: release_handler.erl,v 1.5 2000/09/27 15:04:34 mbj Exp $ %% -module(release_handler). -behaviour(gen_server). %% External exports -export([start_link/0, create_RELEASES/1, create_RELEASES/2, create_RELEASES/4, unpack_release/1, check_install_release/1, install_release/1, install_release/2, remove_release/1, get_release_info/1, which_releases/0, make_permanent/1, reboot_old_release/1, reboot_unpacked_release/1, reboot_release/2, prepare_reboot_release/2, set_unpacked/2, set_removed/1, set_tmp_current/1, install_file/2]). %% Internal exports -export([init/1, handle_call/3, handle_info/2, terminate/2]). %% Internal exports, a client release_handler may call this functions. -export([do_write_release/3, do_copy_file/2, do_copy_files/2, do_copy_files/1, do_rename_files/1, do_remove_files/1, do_write_file/2, do_ensure_RELEASES/1]). -record(state, {unpurged = [], root, rel_dir, releases, timer, start_prg, masters = false, client_dir = false, static_emulator = false, pre_sync_nodes = []}). %%----------------------------------------------------------------- %% status action next_status %% ============================================= %% - unpack unpacked %% unpacked install current %% remove - %% current make_permanent permanent %% install other old %% remove - %% permanent make other permanent old %% install permanent %% old reboot permanen %% install current %% remove - %%----------------------------------------------------------------- %% libs = [{Lib, Vsn, Dir}] -record(release, {name, vsn, erts_vsn, libs = [], status}). -define(timeout, 10000). %%----------------------------------------------------------------- %% Assumes the following file structure: %% root --- lib --- Appl-Vsn1 --- %% | | |- ebin %% | | |_ priv %% | |_ Appl-Vsn2 %% | %% |- bin --- start (default; {sasl, start_prg} overrides %% | |- run_erl %% | |- start_erl (reads start_erl.data) %% | |_ %% | %% |- erts-EVsn1 --- bin --- %% | |- %% | |_ erl %% |- erts-EVsn2 %% | %% |- clients --- ClientName1 --- bin -- start %% %% | | |_ releases --- start_erl.data %% | | |_ Vsn1 -- start.boot %% | |_ ClientName2 %% | %% |- clients --- Type1 --- lib %% %% | | |- erts-EVsn %% | | |- bin -- start %% | | |_ ClientName1 -- releases -- start_erl.data %% | | |_ start.boot (static) %% | | |_ Vsn1 %% | |_ Type2 %% | %% |- releases --- RELEASES %% | |_ %% | | %% | |- start_erl.data (generated by rh) %% | | %% | |_ Vsn1 --- start.boot %% | | |- %% | | |_ relup %% | |_ Vsn2 %% | %% |- log --- erlang.log.N (1 .. 5) %% %% where means 'for example Name', and root is %% init:get_argument(root) %% %% It is configurable where the start file is located, and what it %% is called. %% The paramater is {sasl, start_prg} = File %% It is also configurable where the releases directory is located. %% Default is $ROOT/releases. $RELDIR overrids, and %% {sasl, releases_dir} overrides both. %%----------------------------------------------------------------- start_link() -> gen_server:start_link({local, release_handler}, ?MODULE, [], []). %%----------------------------------------------------------------- %% Args: ReleaseName is the name of the package file %% (without .tar.gz)) %% Purpose: Copies all files in the release package to their %% directories. Checks that all required libs and erts %% files are present. %% Returns: {ok, Vsn} | {error, Reason} %% Reason = {existing_release, Vsn} | %% {no_such_file, File} | %% {bad_rel_file, RelFile} | %% {file_missing, FileName} | (in the tar package) %% exit_reason() %%----------------------------------------------------------------- unpack_release(ReleaseName) -> gen_server:call(release_handler, {unpack_release, ReleaseName}, infinity). %%----------------------------------------------------------------- %% Purpose: Checks the relup script for the specified version. %% The release must be unpacked. %% Returns: {ok, FromVsn, Descr} | {error, Reason} %% Reason = {already_installed, Vsn} | %% {bad_relup_file, RelFile} | %% {no_such_release, Vsn} | %% {no_such_from_vsn, Vsn} | %% exit_reason() %%----------------------------------------------------------------- check_install_release(Vsn) -> gen_server:call(release_handler, {check_install_release, Vsn}, infinity). %%----------------------------------------------------------------- %% Purpose: Executes the relup script for the specified version. %% The release must be unpacked. %% Returns: {ok, FromVsn, Descr} | {error, Reason} %% Reason = {already_installed, Vsn} | %% {bad_relup_file, RelFile} | %% {no_such_release, Vsn} | %% {no_such_from_vsn, Vsn} | %% {illegal_option, Opt}} | %% exit_reason() %%----------------------------------------------------------------- install_release(Vsn) -> gen_server:call(release_handler, {install_release, Vsn, restart, []}, infinity). install_release(Vsn, Opt) -> case check_install_options(Opt, restart, []) of {ok, ErrorAction, InstallOpt} -> gen_server:call(release_handler, {install_release, Vsn, ErrorAction, InstallOpt}, infinity); Error -> Error end. check_install_options([Opt | Opts], ErrAct, InstOpts) -> case install_option(Opt) of {error_action, EAct} -> check_install_options(Opts, EAct, InstOpts); true -> check_install_options(Opts, ErrAct, [Opt | InstOpts]); false -> {error, {illegal_option, Opt}} end; check_install_options([], ErrAct, InstOpts) -> {ok, ErrAct, InstOpts}. install_option(Opt = {error_action, reboot}) -> Opt; install_option(Opt = {error_action, restart}) -> Opt; install_option({code_change_timeout, TimeOut}) -> check_timeout(TimeOut); install_option({suspend_timeout, TimeOut}) -> check_timeout(TimeOut); install_option(_Opt) -> false. check_timeout(infinity) -> true; check_timeout(Int) when integer(Int), Int > 0 -> true; check_timeout(_Else) -> false. %%----------------------------------------------------------------- %% Purpose: Makes the specified release version be the one that is %% used when the system starts (or restarts). %% The release must be installed (not unpacked). %% Returns: ok | {error, Reason} %% Reason = {bad_status, Status} | %% {no_such_release, Vsn} | %% exit_reason() %%----------------------------------------------------------------- make_permanent(Vsn) -> gen_server:call(release_handler, {make_permanent, Vsn}, infinity). %%----------------------------------------------------------------- %% Purpose: Reboots the system from an old release. %%----------------------------------------------------------------- reboot_old_release(Vsn) -> reboot_release(Vsn, old). reboot_unpacked_release(Vsn) -> reboot_release(Vsn, unpacked). reboot_release(Vsn, Status) -> gen_server:call(release_handler, {reboot_release, Vsn, true, Status}, infinity). %% Leave it up to the caller to perform the reboot %% Status = old | unpacked | any prepare_reboot_release(Vsn, Status) -> gen_server:call(release_handler, {reboot_release, Vsn, false, Status}, infinity). %%----------------------------------------------------------------- %% Purpose: Deletes all files and directories used by the release %% version, that are not used by any other release. %% The release must not be permanent. %% Returns: ok | {error, Reason} %% Reason = {permanent, Vsn} | %%----------------------------------------------------------------- remove_release(Vsn) -> gen_server:call(release_handler, {remove_release, Vsn}, infinity). %%----------------------------------------------------------------- %% Args: RelFile = string() %% Libs = [{Lib, LibVsn, Dir}] %% Lib = LibVsn = Dir = string() %% Purpose: Tells the release handler that a release has been %% unpacked, without using the function unpack_release/1. %% RelFile is an absolute file name including the extension %% .rel. %% The release dir will be created. The necessary files can %% be installed by calling install_file/2. %% The release_handler remebers where all libs are located. %% If remove_release is called later, %% those libs are removed as well (if no other releases uses %% them). %% Returns: ok | {error, Reason} %%----------------------------------------------------------------- set_unpacked(RelFile, LibDirs) -> gen_server:call(release_handler, {set_unpacked, RelFile, LibDirs}). %%----------------------------------------------------------------- %% Args: Vsn = string() %% Purpose: Makes it possible to handle removal of releases %% outside the release_handler. %% This function won't delete any files at all. %% Returns: ok | {error, Reason} %%----------------------------------------------------------------- set_removed(Vsn) -> gen_server:call(release_handler, {set_removed, Vsn}). %%----------------------------------------------------------------- %% Args: Vsn = string() %% Purpose: Makes it possible to handle installation of releases %% outside the release_handler. %% Returns: ok | {error, Reason} %%----------------------------------------------------------------- set_tmp_current(Vsn) -> gen_server:call(release_handler, {set_tmp_current, Vsn}). %%----------------------------------------------------------------- %% Purpose: Makes it possible to install the start.boot, %% sys.config and relup files if they are not part of a %% standard release package. May be used to %% install files that are generated, before install_release %% is called. %% Returns: ok | {error, {no_such_release, Vsn}} %%----------------------------------------------------------------- install_file(Vsn, File) when list(File) -> gen_server:call(release_handler, {install_file, File, Vsn}). %%----------------------------------------------------------------- %% Returns: [{Name, Vsn, [LibName], Status}] %% Status = unpacked | current | permanent | old %%----------------------------------------------------------------- which_releases() -> gen_server:call(release_handler, which_releases). %%----------------------------------------------------------------- %% Returns: {ok, {Name, EVsn, Status}} | %% {error, {no_such_release, Vsn} %% Status = unpacked | current | permanent | old %%----------------------------------------------------------------- get_release_info(Vsn) -> gen_server:call(release_handler, {get_release_info, Vsn}). %%----------------------------------------------------------------- %% check_script(Script, LibDirs) -> ok | {error, Reason} %%----------------------------------------------------------------- check_script(Script, LibDirs) -> release_handler_1:check_script(Script, LibDirs). %%----------------------------------------------------------------- %% eval_script(Script, Apps, LibDirs, Opts) -> {ok, UnPurged} | %% restart_new_emulator | %% {error, Error} %% {'EXIT', Reason} %% If sync_nodes is present, the calling process must have called %% net_kernel:monitor_nodes(true) before calling this function. %% No! No other process than the release_handler can ever call this %% function, if sync_nodes is used. %%----------------------------------------------------------------- eval_script(Script, Apps, LibDirs, Opts) -> catch release_handler_1:eval_script(Script, Apps, LibDirs, Opts). %%----------------------------------------------------------------- %% Func: create_RELEASES(Root, RelFile, LibDirs) -> ok | {error, Reason} %% Types: Root = RelFile = string() %% Purpose: Creates an initial RELEASES file. %%----------------------------------------------------------------- create_RELEASES([Root, RelFile | LibDirs]) -> create_RELEASES(Root, filename:join(Root, "releases"), RelFile, LibDirs). create_RELEASES(Root, RelFile) -> create_RELEASES(Root, filename:join(Root, "releases"), RelFile, []). create_RELEASES(Root, RelDir, RelFile, LibDirs) -> case catch check_rel(Root, RelFile, LibDirs, false) of {error, Reason } -> {error, Reason}; Rel -> Rel2 = Rel#release{status = permanent}, catch write_releases(RelDir, [Rel2], false) end. %%----------------------------------------------------------------- %% Call-back functions from gen_server %%----------------------------------------------------------------- init([]) -> {ok, [[Root]]} = init:get_argument(root), {CliDir, Masters} = is_client(), ReleaseDir = case application:get_env(sasl, releases_dir) of undefined -> case os:getenv("RELDIR") of false -> if CliDir == false -> filename:join([Root, "releases"]); true -> filename:join([CliDir, "releases"]) end; RELDIR -> RELDIR end; {ok, Dir} -> Dir end, Releases = case consult(filename:join(ReleaseDir, "RELEASES"), Masters) of {ok, [Term]} -> transform_release(ReleaseDir, Term, Masters); _ -> {Name, Vsn} = init:script_id(), [#release{name = Name, vsn = Vsn, status = permanent}] end, StartPrg = case application:get_env(start_prg) of {ok, Found2} when list(Found2) -> {do_check, Found2}; _ -> {no_check, filename:join([Root, "bin", "start"])} end, Static = case application:get_env(static_emulator) of {ok, SFlag} when atom(SFlag) -> SFlag; _ -> false end, {ok, #state{root = Root, rel_dir = ReleaseDir, releases = Releases, start_prg = StartPrg, masters = Masters, client_dir = CliDir, static_emulator = Static}}. handle_call({unpack_release, ReleaseName}, _From, S) when S#state.masters == false -> RelDir = S#state.rel_dir, case catch do_unpack_release(S#state.root, RelDir, ReleaseName, S#state.releases) of {ok, NewReleases, Vsn} -> clean_release(RelDir, ReleaseName), {reply, {ok, Vsn}, S#state{releases = NewReleases}}; {error, Reason} -> {reply, {error, Reason}, S}; {'EXIT', Reason} -> {reply, {error, Reason}, S} end; handle_call({unpack_release, ReleaseName}, _From, S) -> {reply, {error, client_node}, S}; handle_call({check_install_release, Vsn}, From, S) -> case catch do_check_install_release(S#state.rel_dir, Vsn, S#state.releases, S#state.masters) of {ok, CurrentVsn, Descr} -> {reply, {ok, CurrentVsn, Descr}, S}; {error, Reason} -> {reply, {error, Reason}, S}; {'EXIT', Reason} -> {reply, {error, Reason}, S} end; handle_call({install_release, Vsn, ErrorAction, Opts}, From, S) -> NS = resend_sync_nodes(S), case catch do_install_release(S, Vsn, Opts) of {ok, NewReleases, CurrentVsn, Descr} -> {reply, {ok, CurrentVsn, Descr}, NS#state{releases=NewReleases}}; {ok, NewReleases, Unpurged, CurrentVsn, Descr} -> Timer = case S#state.timer of undefined -> {ok, Ref} = timer:send_interval(?timeout, timeout), Ref; Ref -> Ref end, NewS = NS#state{releases = NewReleases, unpurged = Unpurged, timer = Timer}, {reply, {ok, CurrentVsn, Descr}, NewS}; {error, Reason} -> {reply, {error, Reason}, NS}; {restart_new_emulator, CurrentVsn, Descr} -> gen_server:reply(From, {ok, CurrentVsn, Descr}), init:reboot(), {noreply, NS}; {'EXIT', Reason} -> gen_server:reply(From, {error, Reason}), case ErrorAction of restart -> init:restart(); reboot -> init:reboot() end, {noreply, NS} end; handle_call({make_permanent, Vsn}, _From, S) -> case catch do_make_permanent(S, Vsn) of {ok, Releases, Unpurged} -> {reply, ok, S#state{releases = Releases, unpurged = Unpurged}}; {error, Reason} -> {reply, {error, Reason}, S}; {'EXIT', Reason} -> {reply, {error, Reason}, S} end; handle_call({reboot_release, Vsn, DoReboot, Status}, From, S) -> case catch do_reboot_release(S, Vsn, Status) of {ok, _Releases} when DoReboot == true -> gen_server:reply(From, ok), init:reboot(), {noreply, S}; {ok, Releases} -> {reply, ok, S#state{releases = Releases}}; {error, Reason} -> {reply, {error, Reason}, S}; {'EXIT', Reason} -> {reply, {error, Reason}, S} end; handle_call({remove_release, Vsn}, _From, S) when S#state.masters == false -> case catch do_remove_release(S#state.root, S#state.rel_dir, Vsn, S#state.releases) of {ok, NewReleases} -> {reply, ok, S#state{releases = NewReleases}}; {error, Reason} -> {reply, {error, Reason}, S}; {'EXIT', Reason} -> {reply, {error, Reason}, S} end; handle_call({remove_release, Vsn}, _From, S) -> {reply, {error, client_node}, S}; handle_call({set_unpacked, RelFile, LibDirs}, _From, S) -> Root = S#state.root, case catch do_set_unpacked(Root, S#state.rel_dir, RelFile, LibDirs, S#state.releases, S#state.masters) of {ok, NewReleases, Vsn} -> {reply, {ok, Vsn}, S#state{releases = NewReleases}}; {error, Reason} -> {reply, {error, Reason}, S}; {'EXIT', Reason} -> {reply, {error, Reason}, S} end; handle_call({set_removed, Vsn}, _From, S) -> Root = S#state.root, case catch do_set_removed(Root, S#state.rel_dir, Vsn, S#state.releases, S#state.masters) of {ok, NewReleases} -> {reply, ok, S#state{releases = NewReleases}}; {error, Reason} -> {reply, {error, Reason}, S}; {'EXIT', Reason} -> {reply, {error, Reason}, S} end; handle_call({set_tmp_current, Vsn}, _From, S) -> Root = S#state.root, case catch do_set_tmp_current(Root, S#state.rel_dir, Vsn, S#state.releases, S#state.masters) of {ok, NewReleases} -> {reply, ok, S#state{releases = NewReleases}}; {error, Reason} -> {reply, {error, Reason}, S}; {'EXIT', Reason} -> {reply, {error, Reason}, S} end; handle_call({install_file, File, Vsn}, _From, S) -> Reply = case lists:keysearch(Vsn, #release.vsn, S#state.releases) of {value, _} -> Dir = filename:join([S#state.rel_dir, Vsn]), catch copy_file(File, Dir, S#state.masters); _ -> {error, {no_such_release, Vsn}} end, {reply, Reply, S}; handle_call(which_releases, _From, S) -> Reply = lists:map(fun(#release{name = Name, vsn = Vsn, libs = Libs, status = Status}) -> {Name, Vsn, mk_lib_name(Libs), Status} end, S#state.releases), {reply, Reply, S}; handle_call({get_release_info, Vsn}, _From, S) -> Reply = case lists:keysearch(Vsn, #release.vsn, S#state.releases) of {value, #release{name = Name, erts_vsn = EVsn, status = Status}} -> {ok, {Name, EVsn, Status}}; _ -> {error, {no_such_release, Vsn}} end, {reply, Reply, S}. mk_lib_name([{LibName, Vsn, _Dir} | T]) -> [lists:concat([LibName, "-", Vsn]) | mk_lib_name(T)]; mk_lib_name([]) -> []. handle_info(timeout, S) -> case soft_purge(S#state.unpurged) of [] -> timer:cancel(S#state.timer), {noreply, S#state{unpurged = [], timer = undefined}}; Unpurged -> {noreply, S#state{unpurged = Unpurged}} end; handle_info({sync_nodes, Id, Node}, S) -> PSN = S#state.pre_sync_nodes, {noreply, S#state{pre_sync_nodes = [{sync_nodes, Id, Node} | PSN]}}; handle_info(Msg, S) -> error_logger:info_msg("release_handler: got unknown message: ~p~n", [Msg]), {noreply, S}. terminate(_Reason, S) -> ok. %%%----------------------------------------------------------------- %%% Internal functions %%%----------------------------------------------------------------- is_client() -> case application:get_env(masters) of {ok, Masters} -> Alive = is_alive(), case atom_list(Masters) of true when Alive == true -> case application:get_env(client_directory) of {ok, ClientDir} -> case int_list(ClientDir) of true -> {ClientDir, Masters}; _ -> exit({bad_parameter, client_directory, ClientDir}) end; _ -> {false, false} end; _ -> exit({bad_parameter, masters, Masters}) end; _ -> {false, false} end. atom_list([A|T]) when atom(A) -> atom_list(T); atom_list([]) -> true; atom_list(_) -> false. int_list([I|T]) when integer(I) -> int_list(T); int_list([]) -> true; int_list(_) -> false. resend_sync_nodes(S) -> lists:foreach(fun(Msg) -> self() ! Msg end, S#state.pre_sync_nodes), S#state{pre_sync_nodes = []}. soft_purge(Unpurged) -> lists:filter(fun({Mod, _PostPurgeMethod}) -> case code:soft_purge(Mod) of true -> false; % No proc left, don't remember Mod false -> true % Still proc left, remember it end end, Unpurged). brutal_purge(Unpurged) -> lists:filter(fun({Mod, brutal_purge}) -> code:purge(Mod), false; (_) -> true end, Unpurged). %%----------------------------------------------------------------- %% The release package is a RelName.tar.gz file %% with the following contents: %% - RelName.rel == {release, {Name, Vsn}, {erts, EVsn}, [lib()]} %% - according to [lib()] %% - lib() = {LibName, LibVsn} %% In the Dir, there exists a file called RELEASES, which contains %% a [{Vsn, {erts, EVsn}, {libs, [{LibName, LibVsn, LibDir}]}}]. %% Note that RelDir is an absolute directory name ! %% Note that this function is not executed by a client %% release_handler. %%----------------------------------------------------------------- do_unpack_release(Root, RelDir, ReleaseName, Releases) -> Tar = filename:join(RelDir, ReleaseName ++ ".tar.gz"), do_check_file(Tar, regular), Rel = ReleaseName ++ ".rel", extract_rel_file(filename:join("releases", Rel), Tar, Root), RelFile = filename:join(RelDir, Rel), Release = check_rel(Root, RelFile, false), #release{vsn = Vsn, erts_vsn = EVsn} = Release, case lists:keysearch(Vsn, #release.vsn, Releases) of {value, _} -> throw({error, {existing_release, Vsn}}); _ -> ok end, extract_tar(Root, Tar), NewReleases = [Release#release{status = unpacked} | Releases], write_releases(RelDir, NewReleases, false), Dir = filename:join([RelDir, Vsn]), copy_file(RelFile, Dir, false), {ok, NewReleases, Vsn}. %% Note that this function is not executed by a client %% release_handler. clean_release(RelDir, ReleaseName) -> Tar = filename:join(RelDir, ReleaseName ++ ".tar"), Rel = filename:join(RelDir, ReleaseName ++ ".rel"), file:delete(Tar), file:delete(Rel), case os:type() of {unix, _} -> Z = Tar ++ ".gz", file:delete(Z); _ -> ok end. check_rel(Root, RelFile, Masters) -> check_rel(Root, RelFile, [], Masters). check_rel(Root, RelFile, LibDirs, Masters) -> case consult(RelFile, Masters) of {ok, [RelData]} -> check_rel_data(RelData, Root, LibDirs); {ok, _} -> throw({error, {bad_rel_file, RelFile}}); {error, {LineNo, Mod, Reason}} -> throw({error, {bad_rel_file, RelFile}}); {error, FileError} -> % FileError is posix atom | no_master throw({error, {FileError, RelFile}}) end. check_rel_data({release, {Name, Vsn}, {erts, EVsn}, Libs}, Root, LibDirs) -> Libs2 = lists:map(fun(LibSpec) -> case LibSpec of {Lib, LibVsn} -> ok; {Lib, LibVsn, _Subapps} -> ok; {Lib, LibVsn, _AppType, _Subapps} -> ok end, LibName = lists:concat([Lib, "-", LibVsn]), LibDir = case lists:keysearch(Lib, 1, LibDirs) of {value, {_Lib, _Vsn, Dir}} -> filename:join(Dir, LibName); _ -> filename:join([Root, "lib", LibName]) end, {Lib, LibVsn, LibDir} end, Libs), #release{name = Name, vsn = Vsn, erts_vsn = EVsn, libs = Libs2, status = unpacking}; check_rel_data(RelData, _Root, _LibDirs) -> throw({error, {bad_rel_data, RelData}}). do_check_install_release(RelDir, Vsn, Releases, Masters) -> LatestRelease = get_latest_release(Releases), case lists:keysearch(Vsn, #release.vsn, Releases) of {value, LatestRelease} -> {error, {already_installed, Vsn}}; {value, Release} -> VsnDir = filename:join([RelDir, Vsn]), check_file(filename:join(VsnDir, "start.boot"), regular, Masters), % check_file(filename:join(VsnDir, "relup"), regular, Masters), check_file(filename:join(VsnDir, "sys.config"), regular, Masters), %% Check that all required libs are present Libs = Release#release.libs, lists:foreach(fun({_Lib, _LibVsn, LibDir}) -> check_file(LibDir, directory, Masters), Ebin = filename:join(LibDir, "ebin"), check_file(Ebin, directory, Masters) end, Libs), case get_rh_script(LatestRelease, Release, RelDir, Masters) of {ok, {CurrentVsn, Descr, Script}} -> case catch check_script(Script, Libs) of ok -> {ok, CurrentVsn, Descr}; Else -> Else end; Error -> Error end; _ -> {error, {no_such_release, Vsn}} end. do_install_release(#state{start_prg = StartPrg, root = Root, rel_dir = RelDir, releases = Releases, masters = Masters, static_emulator = Static}, Vsn, Opts) -> LatestRelease = get_latest_release(Releases), case lists:keysearch(Vsn, #release.vsn, Releases) of {value, LatestRelease} -> {error, {already_installed, Vsn}}; {value, Release} -> case get_rh_script(LatestRelease, Release, RelDir, Masters) of {ok, {CurrentVsn, Descr, Script}} -> mon_nodes(true), EnvBefore = application_controller:prep_config_change(), Apps = change_appl_data(RelDir, Release, Masters), LibDirs = Release#release.libs, case eval_script(Script, Apps, LibDirs, Opts) of {ok, []} -> application_controller:config_change(EnvBefore), mon_nodes(false), NewReleases = set_status(Vsn, current, Releases), {ok, NewReleases, CurrentVsn, Descr}; {ok, Unpurged} -> application_controller:config_change(EnvBefore), mon_nodes(false), NewReleases = set_status(Vsn, current, Releases), {ok, NewReleases, Unpurged, CurrentVsn, Descr}; restart_new_emulator when Static == true -> throw(static_emulator); restart_new_emulator -> mon_nodes(false), {value, PermanentRelease} = lists:keysearch(permanent, #release.status, Releases), NReleases = set_status(Vsn, current, Releases), NReleases2 = set_status(Vsn,tmp_current,NReleases), write_releases(RelDir, NReleases2, Masters), prepare_restart_new_emulator(StartPrg, RelDir, Release, PermanentRelease, Masters), {restart_new_emulator, CurrentVsn, Descr}; Else -> application_controller:config_change(EnvBefore), mon_nodes(false), Else end; Error -> Error end; _ -> {error, {no_such_release, Vsn}} end. %%% This code chunk updates the services in one of two ways, %%% Either the emulator is restarted, in which case the old service %%% is to be removed and the new enabled, or the emulator is NOT restarted %%% in which case we try to rename the old service to the new name and try %%% to update heart's view of what service we are really running. do_make_services_permanent(PermanentVsn,Vsn, PermanentEVsn, EVsn) -> PermName = hd(string:tokens(atom_to_list(node()),"@")) ++ "_" ++ PermanentVsn, Name = hd(string:tokens(atom_to_list(node()),"@")) ++ "_" ++ Vsn, case erlsrv:get_service(EVsn,Name) of {error, Error} -> %% We probably do not need to replace services, just %% rename. case os:getenv("ERLSRV_SERVICE_NAME") == PermName of true -> case erlsrv:rename_service(EVsn,PermName,Name) of {ok,_} -> case erlsrv:get_service(EVsn,Name) of {error,Error2} -> throw({error,Error2}); Data2 -> %% The interfaces for doing this are %% NOT published and may be subject to %% change. Do NOT do this anywhere else! os:putenv("ERLSRV_SERVICE_NAME", Name), %% Restart heart port program, this %% function is only to be used here. heart:cycle(), ok end; Error3 -> throw({error,{service_rename_failed, Error3}}) end; false -> throw({error,service_name_missmatch}) end; Data -> UpdData = erlsrv:new_service(Name, Data, []), case erlsrv:store_service(EVsn,UpdData) of ok -> erlsrv:disable_service(PermanentEVsn, PermName), erlsrv:enable_service(EVsn, Name), erlsrv:remove_service(PermName), os:putenv("ERLSRV_SERVICE_NAME", Name), heart:cycle(), ok; Error4 -> throw(Error4) end end. do_make_permanent(#state{root = Root, releases = Releases, rel_dir = RelDir, unpurged = Unpurged, masters = Masters, client_dir = CliDir, static_emulator = Static}, Vsn) -> case lists:keysearch(Vsn, #release.vsn, Releases) of {value, #release{erts_vsn = EVsn, status = Status}} when Status /= unpacked, Status /= old, Status /= permanent -> Dir = filename:join([RelDir, Vsn]), Sys = case catch check_file(filename:join(Dir, "sys.config"), regular, Masters) of ok -> filename:join(Dir, "sys"); _ -> false end, Boot = filename:join(Dir, "start.boot"), check_file(Boot, regular, Masters), set_permanent_files(RelDir, EVsn, Vsn, Masters, CliDir, Static), NewReleases = set_status(Vsn, permanent, Releases), write_releases(RelDir, NewReleases, Masters), case os:type() of {win32, nt} -> {value, PermanentRelease} = lists:keysearch(permanent, #release.status, Releases), PermanentVsn = PermanentRelease#release.vsn, PermanentEVsn = PermanentRelease#release.erts_vsn, case catch do_make_services_permanent(PermanentVsn, Vsn, PermanentEVsn, EVsn) of {error,Reason} -> {error,{service_update_failed, Reason}}; _ -> ok end; _ -> ok end, init:make_permanent(filename:join(Dir, "start"), Sys), {ok, NewReleases, brutal_purge(Unpurged)}; {value, #release{status = permanent}} -> {ok, Releases, Unpurged}; {value, #release{status = Status}} -> {error, {bad_status, Status}}; false -> {error, {no_such_release, Vsn}} end. do_back_service(OldVersion, CurrentVersion,OldEVsn,CurrentEVsn) -> NN = hd(string:tokens(atom_to_list(node()),"@")), OldName = NN ++ "_" ++ OldVersion, CurrentName = NN ++ "_" ++ CurrentVersion, UpdData = case erlsrv:get_service(CurrentEVsn,CurrentName) of {error, Error} -> throw({error,Error}); Data -> erlsrv:new_service(OldName, Data, []) end, case erlsrv:store_service(OldEVsn,UpdData) of ok -> erlsrv:disable_service(CurrentEVsn,CurrentName), erlsrv:enable_service(OldEVsn,OldName); Error2 -> throw(Error2) end, OldErlSrv = filename:nativename(erlsrv:erlsrv(OldEVsn)), CurrentErlSrv = filename:nativename(erlsrv:erlsrv(CurrentEVsn)), case heart:set_cmd(CurrentErlSrv ++ " remove " ++ CurrentName ++ " & " ++ OldErlSrv ++ " start " ++ OldName) of ok -> ok; Error3 -> throw({error, {'heart:set_cmd() error', Error3}}) end. do_reboot_release(#state{root = Root, releases = Releases, rel_dir = RelDir, masters = Masters, client_dir = CliDir, static_emulator = Static}, Vsn, Status) -> case lists:keysearch(Vsn, #release.vsn, Releases) of {value, #release{erts_vsn = EVsn, status = Status2}} when Status == Status2; Status == any -> CurrentRunning = case os:type() of {win32,nt} -> %% Get the current release on NT case lists:keysearch(permanent, #release.status, Releases) of false -> lists:keysearch(current, #release.status, Releases); {value,CR} -> CR end; _ -> false end, set_permanent_files(RelDir, EVsn, Vsn, Masters, CliDir, Static), NewReleases = set_status(Vsn, permanent, Releases), write_releases(RelDir, NewReleases, Masters), case os:type() of {win32,nt} -> %% Edit up the services and set a reasonable heart %% command do_back_service(Vsn,CurrentRunning#release.vsn,EVsn, CurrentRunning#release.erts_vsn); _ -> ok end, {ok, NewReleases}; {value, #release{status = Status2}} -> {error, {bad_status, Status2}}; false -> {error, {no_such_release, Vsn}} end. %%----------------------------------------------------------------- %% Depending of if the release_handler is running in normal, client or %% client with static emulator the new system version is made permanent %% in different ways. %%----------------------------------------------------------------- set_permanent_files(RelDir, EVsn, Vsn, false, _, _) -> write_start(filename:join([RelDir, "start_erl.data"]), EVsn ++ " " ++ Vsn, false); set_permanent_files(RelDir, EVsn, Vsn, Masters, CliDir, false) -> write_start(filename:join([RelDir, "start_erl.data"]), EVsn ++ " " ++ Vsn, Masters); set_permanent_files(RelDir, EVsn, Vsn, Masters, CliDir, Static) -> VsnDir = filename:join([RelDir, Vsn]), set_static_files(VsnDir, RelDir, Masters). do_remove_service(Vsn) -> %%% Very unconditionally remove the service. ServiceName = hd(string:tokens(atom_to_list(node()),"@")) ++ "_" ++ Vsn, erlsrv:remove_service(ServiceName). do_remove_release(Root, RelDir, Vsn, Releases) -> % Decide which libs should be removed case lists:keysearch(Vsn, #release.vsn, Releases) of {value, #release{status = permanent}} -> {error, {permanent, Vsn}}; {value, #release{libs = RemoveLibs, vsn = Vsn, erts_vsn = EVsn}} -> case os:type() of {win32, nt} -> do_remove_service(Vsn); _ -> ok end, NewReleases = lists:keydelete(Vsn, #release.vsn, Releases), RemoveThese = lists:foldl(fun(#release{libs = Libs, vsn = Vsn2}, Remove) -> diff_dir(Remove, Libs) end, RemoveLibs, NewReleases), lists:foreach(fun({_Lib, _LVsn, LDir}) -> remove_file(LDir) end, RemoveThese), remove_file(filename:join([RelDir, Vsn])), case lists:keysearch(EVsn, #release.erts_vsn, NewReleases) of {value, _} -> ok; false -> % Remove erts library, no more references to it remove_file(filename:join(Root, "erts-" ++ EVsn)) end, write_releases(RelDir, NewReleases, false), {ok, NewReleases}; false -> {error, {no_such_release, Vsn}} end. do_set_unpacked(Root, RelDir, RelFile, LibDirs, Releases, Masters) -> Release = check_rel(Root, RelFile, LibDirs, Masters), #release{vsn = Vsn, erts_vsn = EVsn, name = Name} = Release, case lists:keysearch(Vsn, #release.vsn, Releases) of {value, #release{status = unpacked}} -> throw({error, {already_unpacked, Vsn}}); {value, _} -> throw({error, {existing_release, Vsn}}); false -> ok end, NewReleases = [Release#release{status = unpacked} | Releases], VsnDir = filename:join([RelDir, Vsn]), make_dir(VsnDir, Masters), write_releases(RelDir, NewReleases, Masters), {ok, NewReleases, Vsn}. do_set_removed(Root, RelDir, Vsn, Releases, Masters) -> case lists:keysearch(Vsn, #release.vsn, Releases) of {value, #release{status = permanent}} -> {error, {permanent, Vsn}}; {value, _} -> NewReleases = lists:keydelete(Vsn, #release.vsn, Releases), write_releases(RelDir, NewReleases, Masters), {ok, NewReleases}; false -> {error, {no_such_release, Vsn}} end. do_set_tmp_current(Root, RelDir, Vsn, Releases, Masters) -> %% Currently 'current' gets 'old'... NReleases0 = set_status(Vsn, current, Releases), %% Vsn gets 'tmp_current' NReleases1 = set_status(Vsn, tmp_current_old, NReleases0), write_releases(RelDir, NReleases1, Masters), {ok, NReleases1}. %%----------------------------------------------------------------- %% A relup file consists of: %% {Vsn, [{FromVsn, Descr, RhScript}], [{ToVsn, Descr, RhScript}]}. %% It describes how to get to this release from previous releases, %% and how to get from this release to previous releases. %% We can get from a FromVsn that's a substring of CurrentVsn (e.g. %% 1.1 is a substring of 1.1.1, but not 1.2), but when we get to %% ToVsn, we must have an exact match. %% %% We do not put any semantics into the version strings, i.e. we %% don't know if going from Vsn1 to Vsn2 represents a upgrade or %% a downgrade. For both upgrades and downgrades, the relup file %% is located in the directory of the latest version. Since we %% do not which version is latest, we first suppose that ToVsn > %% CurrentVsn, i.e. we perform an upgrade. If we don't find the %% corresponding relup instructions, we check if it's possible to %% downgrade from CurrentVsn to ToVsn. %%----------------------------------------------------------------- get_rh_script(#release{vsn = CurrentVsn}, #release{vsn = Vsn}, RelDir, Masters) -> Relup = filename:join([RelDir, Vsn, "relup"]), case try_upgrade(Vsn, CurrentVsn, Relup, Masters) of {ok, RhScript} -> {ok, RhScript}; _ -> Relup2 = filename:join([RelDir, CurrentVsn,"relup"]), case try_downgrade(Vsn, CurrentVsn, Relup2, Masters) of {ok, RhScript} -> {ok, RhScript}; _ -> throw({error, {no_matching_relup, Vsn, CurrentVsn}}) end end. try_upgrade(ToVsn, CurrentVsn, Relup, Masters) -> case consult(Relup, Masters) of {ok, [{Vsn, ListOfRhScripts, _}]} -> case find_matching_vsn(ListOfRhScripts, CurrentVsn) of {ok, RhScript} -> {ok, RhScript}; false -> error end; {ok, _} -> throw({error, {bad_relup_file, Relup}}); {error, {LineNo, Mod, Reason}} -> throw({error, {bad_relup_file, Relup}}); {error, enoent} -> error; {error, FileError} -> % FileError is posix atom | no_master throw({error, {FileError, Relup}}) end. try_downgrade(ToVsn, CurrentVsn, Relup, Masters) -> case consult(Relup, Masters) of {ok, [{CurrentVsn, _, ListOfRhScripts}]} -> case lists:keysearch(ToVsn, 1, ListOfRhScripts) of {value, RhScript} -> {ok, RhScript}; _ -> error end; {ok, _} -> throw({error, {bad_relup_file, Relup}}); {error, {LineNo, Mod, Reason}} -> throw({error, {bad_relup_file, Relup}}); {error, enoent} -> error; {error, FileError} -> % FileError is posix atom | no_master throw({error, {FileError, Relup}}) end. find_matching_vsn(ListOfRhScripts, CurrentVsn) -> fmv(lists:reverse(lists:keysort(1, ListOfRhScripts)), CurrentVsn). fmv([{FromVsn, Descr, RhScript} | T], CurrentVsn) -> case string:re_match(CurrentVsn, [$^|FromVsn]) of {match, _, _} -> {ok, {FromVsn, Descr, RhScript}}; _ -> fmv(T, CurrentVsn) end; fmv([H | _], CurrentVsn) -> throw({error, {bad_relup_syntax, H}}); fmv([], _) -> false. %% Status = current | tmp_current | permanent set_status(Vsn, Status, Releases) -> lists:zf(fun(Release) when Release#release.vsn == Vsn, Release#release.status == permanent -> %% If a permanent rel is installed, it keeps its %% permanent status (not changed to current). %% The current becomes old though. true; (Release) when Release#release.vsn == Vsn -> {true, Release#release{status = Status}}; (Release) when Release#release.status == Status -> {true, Release#release{status = old}}; (_) -> true end, Releases). get_latest_release(Releases) -> case lists:keysearch(current, #release.status, Releases) of {value, Release} -> Release; false -> {value, Release} = lists:keysearch(permanent, #release.status, Releases), Release end. %% Returns: [{Lib, Vsn, Dir}] to be removed diff_dir([H | T], L) -> case memlib(H, L) of true -> diff_dir(T, L); false -> [H | diff_dir(T, L)] end; diff_dir([], _) -> []. memlib({Lib, Vsn, _Dir}, [{Lib, Vsn, _Dir2} | T]) -> true; memlib(Lib, [H | T]) -> memlib(Lib, T); memlib(Lib, []) -> false. %% recursively remove file or directory remove_file(File) -> case file:file_info(File) of {ok, Info} when element(2, Info) == directory -> case file:list_dir(File) of {ok, Files} -> lists:foreach(fun(File2) -> remove_file(filename:join(File,File2)) end, Files), case file:del_dir(File) of ok -> ok; {error, Reason} -> throw({error, Reason}) end; {error, Reason} -> throw({error, Reason}) end; {ok, _Info} -> case file:delete(File) of ok -> ok; {error, Reason} -> throw({error, Reason}) end; _ -> throw({error, {no_such_file, File}}) end. do_write_file(File, Str) -> case file:open(File, write) of {ok, Fd} -> case io:put_chars(Fd, Str) of ok -> file:close(Fd), ok; {error, Reason} -> file:close(Fd), {error, {Reason, File}} end; {error, Reason} -> {error, {Reason, File}} end. %%----------------------------------------------------------------- %% Change current applications (specifically, update their version, %% description and env.) %%----------------------------------------------------------------- change_appl_data(RelDir, #release{vsn = Vsn}, Masters) -> Dir = filename:join([RelDir, Vsn]), BootFile = filename:join(Dir, "start.boot"), case read_file(BootFile, Masters) of {ok, Bin} -> Config = case consult(filename:join(Dir, "sys.config"), Masters) of {ok, [Conf]} -> Conf; _ -> [] end, Appls = get_appls(binary_to_term(Bin)), case application_controller:change_application_data(Appls,Config) of ok -> Appls; {error, Reason} -> exit({change_appl_data, Reason}) end; {error, Reason} -> throw({error, {no_such_file, BootFile}}) end. %%----------------------------------------------------------------- %% This function is dependent on the application functions and %% the start script syntax. %%----------------------------------------------------------------- get_appls({script, _, Script}) -> get_appls(Script, []). %% kernel is taken care of separately get_appls([{kernelProcess, application_controller, {application_controller, start, [App]}} |T], Res) -> get_appls(T, [App | Res]); %% other applications but kernel get_appls([{apply, {application, load, [App]}} |T], Res) -> get_appls(T, [App | Res]); get_appls([_ | T], Res) -> get_appls(T, Res); get_appls([], Res) -> Res. mon_nodes(true) -> net_kernel:monitor_nodes(true); mon_nodes(false) -> net_kernel:monitor_nodes(false), flush(). flush() -> receive {nodedown, _} -> flush(); {nodeup, _} -> flush() after 0 -> ok end. prepare_restart_nt(#release{erts_vsn = EVsn, vsn = Vsn}, #release{erts_vsn = PermEVsn, vsn = PermVsn}, DataFileName) -> CurrentServiceName = hd(string:tokens(atom_to_list(node()),"@")) ++ "_" ++ PermVsn, FutureServiceName = hd(string:tokens(atom_to_list(node()),"@")) ++ "_" ++ Vsn, CurrentService = case erlsrv:get_service(PermEVsn,CurrentServiceName) of {error, Reason} -> throw({error, Reason}); CS -> CS end, FutureService = erlsrv:new_service(FutureServiceName, CurrentService, filename:nativename(DataFileName), %% This is rather icky... On a %% non permanent service, the %% ERLSRV_SERVICE_NAME is %% actually that of an old service, %% to make heart commands work... CurrentServiceName), case erlsrv:store_service(EVsn, FutureService) of {error, Rison} -> throw({error,Rison}); _ -> erlsrv:disable_service(EVsn, FutureServiceName), ErlSrv = filename:nativename(erlsrv:erlsrv(EVsn)), case heart:set_cmd(ErlSrv ++ " enable " ++ FutureServiceName ++ " & " ++ ErlSrv ++ " start " ++ FutureServiceName ++ " & " ++ ErlSrv ++ " disable " ++ FutureServiceName) of ok -> ok; Error -> throw({error, {'heart:set_cmd() error', Error}}) end end. %%----------------------------------------------------------------- %% Set things up for restarting the new emulator. The actual %% restart is performed by calling init:reboot() higher up. %%----------------------------------------------------------------- prepare_restart_new_emulator(StartPrg, RelDir, Release, PRelease, Masters) -> #release{erts_vsn = EVsn, vsn = Vsn} = Release, Data = EVsn ++ " " ++ Vsn, DataFile = write_new_start_erl(Data, RelDir, Masters), %% Tell heart to use DataFile instead of start_erl.data case os:type() of {win32,nt} -> prepare_restart_nt(Release,PRelease,DataFile); {unix,_} -> StartP = check_start_prg(StartPrg, Masters), case heart:set_cmd(StartP ++ " " ++ DataFile) of ok -> ok; Error -> throw({error, {'heart:set_cmd() error', Error}}) end end. check_start_prg({do_check, StartPrg}, Masters) -> {ok, [Cmd | _Args]} = regexp:split(StartPrg, " "), check_file(Cmd, regular, Masters), StartPrg; check_start_prg({_, StartPrg}, _) -> StartPrg. write_new_start_erl(Data, RelDir, false) -> DataFile = filename:join([RelDir, "new_start_erl.data"]), case do_write_file(DataFile, Data) of ok -> DataFile; Error -> throw(Error) end; write_new_start_erl(Data, RelDir, Masters) -> DataFile = filename:join([RelDir, "new_start_erl.data"]), case at_all_masters(Masters, ?MODULE, do_write_file, [DataFile, Data]) of ok -> DataFile; Error -> throw(Error) end. %%----------------------------------------------------------------- %% When a new emulator shall be restarted, the current release %% is written with status tmp_current. When the new emulator %% is started, this function is called. The tmp_current release %% gets status unpacked on disk, and current in memory. If a reboot %% is made (due to a crash), the release is just unpacked. If a crash %% occurs before a call to transform_release is made, the old emulator %% is started, and transform_release is called for it. The tmp_current %% release is changed to unpacked. %% If the release is made permanent, this is written to disk. %%----------------------------------------------------------------- transform_release(ReleaseDir, Releases, Masters) -> F = fun(Release) when Release#release.status == tmp_current -> Release#release{status = unpacked}; (Release) when Release#release.status == tmp_current_old -> Release#release{status = old}; (Release) -> Release end, case lists:map(F, Releases) of Releases -> Releases; DReleases -> write_releases(ReleaseDir, DReleases, Masters), F1 = fun(Release) when Release#release.status == tmp_current -> case init:script_id() of {Name, Vsn} when Release#release.vsn == Vsn -> Release#release{status = current}; _ -> Release#release{status = unpacked} end; (Release) when Release#release.status == tmp_current_old -> case init:script_id() of {Name, Vsn} when Release#release.vsn == Vsn -> Release#release{status = current}; _ -> Release#release{status = old} end; (Release) -> Release end, lists:map(F1, Releases) end. %%----------------------------------------------------------------- %% Functions handling files, RELEASES, start_erl.data etc. %% This functions consider if the release_handler is a client and %% in that case performs the operations at all master nodes or at %% none (in case of failure). %%----------------------------------------------------------------- check_file(FileName, Type, false) -> do_check_file(FileName, Type); check_file(FileName, Type, Masters) -> check_file_masters(FileName, Type, Masters). %% Check that file exists at all masters. check_file_masters(FileName, Type, [Master|Masters]) -> do_check_file(Master, FileName, Type), check_file_masters(FileName, Type, Masters); check_file_masters(FileName, Type, []) -> ok. %% Type == regular | directory do_check_file(FileName, Type) -> case file:file_info(FileName) of {ok,{_,Type,_,_,_,_,_}} -> ok; {error, _Reason} -> throw({error, {no_such_file, FileName}}) end. do_check_file(Master, FileName, Type) -> case rpc:call(Master, file, file_info, [FileName]) of {ok,{_,Type,_,_,_,_,_}} -> ok; _ -> throw({error, {no_such_file, {Master, FileName}}}) end. %%----------------------------------------------------------------- %% If Rel doesn't exists in tar it could have been created %% by the user in another way, i.e. ignore this here. %%----------------------------------------------------------------- extract_rel_file(Rel, Tar, Root) -> erl_tar:extract(Tar, [{files, [Rel]}, {cwd, Root}, compressed]). extract_tar(Root, Tar) -> case erl_tar:extract(Tar, [keep_old_files, {cwd, Root}, compressed]) of ok -> ok; {error, Reason, Name} -> % Old erl_tar. throw({error, {cannot_extract_file, Name, Reason}}); {error, {Name, Reason}} -> % New erl_tar (R3A). throw({error, {cannot_extract_file, Name, Reason}}) end. write_releases(Dir, NewReleases, false) -> case do_write_release(Dir, "RELEASES", NewReleases) of ok -> ok; Error -> throw(Error) end; write_releases(Dir, NewReleases, Masters) -> all_masters(Masters), write_releases_m(Dir, NewReleases, Masters). do_write_release(Dir, RELEASES, NewReleases) -> case file:open(filename:join(Dir, RELEASES), write) of {ok, Fd} -> ok = io:format(Fd, "~p.~n", [NewReleases]), file:close(Fd), ok; {error, Reason} -> {error, {write_releases, Reason}} end. %%----------------------------------------------------------------- %% Write the "RELEASES" file at all master nodes. %% 1. Save "RELEASES.backup" at all nodes. %% 2. Save "RELEASES.change" at all nodes. %% 3. Update the "RELEASES.change" file at all nodes. %% 4. Move "RELEASES.change" to "RELEASES". %% 5. Remove "RELEASES.backup" at all nodes. %% %% If one of the steps above fails, all steps is recovered from %% (as long as possible), except for 5 which is allowed to fail. %%----------------------------------------------------------------- write_releases_m(Dir, NewReleases, Masters) -> RelFile = filename:join(Dir, "RELEASES"), Backup = filename:join(Dir, "RELEASES.backup"), Change = filename:join(Dir, "RELEASES.change"), ensure_RELEASES_exists(Masters, RelFile), case at_all_masters(Masters, ?MODULE, do_copy_files, [RelFile, [Backup, Change]]) of ok -> case at_all_masters(Masters, ?MODULE, do_write_release, [Dir, "RELEASES.change", NewReleases]) of ok -> case at_all_masters(Masters, file, rename, [Change, RelFile]) of ok -> remove_files(all, [Backup, Change], Masters), ok; {error, {Master, R}} -> takewhile(Master, Masters, file, rename, [Backup, RelFile]), remove_files(all, [Backup, Change], Masters), throw({error, {Master, R, move_releases}}) end; {error, {Master, R}} -> remove_files(all, [Backup, Change], Masters), throw({error, {Master, R, update_releases}}) end; {error, {Master, R}} -> remove_files(Master, [Backup, Change], Masters), throw({error, {Master, R, backup_releases}}) end. ensure_RELEASES_exists(Masters, RelFile) -> case at_all_masters(Masters, ?MODULE, do_ensure_RELEASES, [RelFile]) of ok -> ok; {error, {Master, R}} -> throw({error, {Master, R, ensure_RELEASES_exists}}) end. copy_file(File, Dir, false) -> case do_copy_file(File, Dir) of ok -> ok; Error -> throw(Error) end; copy_file(File, Dir, Masters) -> all_masters(Masters), copy_file_m(File, Dir, Masters). %%----------------------------------------------------------------- %% copy File to Dir at every master node. %% If an error occurs at a node, the total copy failed. %% We do not have to cleanup in case of failure as this %% copy_file is harmless. %%----------------------------------------------------------------- copy_file_m(File, Dir, [Master|Masters]) -> case rpc:call(Master, ?MODULE, do_copy_file, [File, Dir]) of ok -> copy_file_m(File, Dir, Masters); {error, {Reason, F}} -> throw({error, {Master, Reason, F}}); Other -> throw({error, {Master, Other, File}}) end; copy_file_m(File, Dir, []) -> ok. do_copy_file(File, Dir) -> File2 = filename:join(Dir, filename:basename(File)), do_copy_file1(File, File2). do_copy_file1(File, File2) -> case file:read_file(File) of {ok, Bin} -> case file:write_file(File2, Bin) of ok -> ok; {error, Reason} -> {error, {Reason, File2}} end; {error, Reason} -> {error, {Reason, File}} end. %%----------------------------------------------------------------- %% Copy File to a list of files. %%----------------------------------------------------------------- do_copy_files(File, [ToFile|ToFiles]) -> case do_copy_file1(File, ToFile) of ok -> do_copy_files(File, ToFiles); Error -> Error end; do_copy_files(_, []) -> ok. %%----------------------------------------------------------------- %% Copy each Src file to Dest file in the list of files. %%----------------------------------------------------------------- do_copy_files([{Src, Dest}|Files]) -> case do_copy_file1(Src, Dest) of ok -> do_copy_files(Files); Error -> Error end; do_copy_files([]) -> ok. %%----------------------------------------------------------------- %% Rename each Src file to Dest file in the list of files. %%----------------------------------------------------------------- do_rename_files([{Src, Dest}|Files]) -> case file:rename(Src, Dest) of ok -> do_rename_files(Files); Error -> Error end; do_rename_files([]) -> ok. %%----------------------------------------------------------------- %% Remove a list of files. Ignore failure. %%----------------------------------------------------------------- do_remove_files([File|Files]) -> file:delete(File), do_remove_files(Files); do_remove_files([]) -> ok. %%----------------------------------------------------------------- %% Ensure that the RELEASES file exists. %% If not create an empty RELEASES file. %%----------------------------------------------------------------- do_ensure_RELEASES(RelFile) -> case file:file_info(RelFile) of {ok, _} -> ok; _ -> do_write_file(RelFile, "[]. ") end. %%----------------------------------------------------------------- %% Make a directory, ignore failures (captured later). %%----------------------------------------------------------------- make_dir(Dir, false) -> file:make_dir(Dir); make_dir(Dir, Masters) -> lists:foreach(fun(Master) -> rpc:call(Master, file, make_dir, [Dir]) end, Masters). %%----------------------------------------------------------------- %% Check that all masters are alive. %%----------------------------------------------------------------- all_masters(Masters) -> case rpc:multicall(Masters, erlang, info, [version]) of {_, []} -> ok; {_, BadNodes} -> throw({error, {bad_masters, BadNodes}}) end. %%----------------------------------------------------------------- %% Evaluate {M,F,A} at all masters. %% {M,F,A} is supposed to return ok. Otherwise at_all_masters %% returns {error, {Master, Other}}. %%----------------------------------------------------------------- at_all_masters([Master|Masters], M, F, A) -> case rpc:call(Master, M, F, A) of ok -> at_all_masters(Masters, M, F, A); Error -> {error, {Master, Error}} end; at_all_masters([], _, _, _) -> ok. %%----------------------------------------------------------------- %% Evaluate {M,F,A} at all masters until Master is found. %% Ignore {M,F,A} return value. %%----------------------------------------------------------------- takewhile(Master, Masters, M, F, A) -> lists:takewhile(fun(Ma) when Ma == Master -> false; (Ma) -> rpc:call(Ma, M, F, A), true end, Masters), ok. consult(File, false) -> file:consult(File); consult(File, Masters) -> consult_master(Masters, File). %%----------------------------------------------------------------- %% consult the File at any master node. %% If the file does not exist at one node it should %% not exist at any other node either. %%----------------------------------------------------------------- consult_master([Master|Ms], File) -> case rpc:call(Master, file, consult, [File]) of {badrpc, _} -> consult_master(Ms, File); Res -> Res end; consult_master([], File) -> {error, no_master}. read_file(File, false) -> file:read_file(File); read_file(File, Masters) -> read_master(Masters, File). %% Ignore status of each delete ! remove_files(Master, Files, Masters) -> takewhile(Master, Masters, ?MODULE, do_remove_files, [Files]). %%----------------------------------------------------------------- %% read the File at any master node. %% If the file does not exist at one node it should %% not exist at any other node either. %%----------------------------------------------------------------- read_master([Master|Ms], File) -> case rpc:call(Master, file, read_file, [File]) of {badrpc, _} -> read_master(Ms, File); Res -> Res end; read_master([], File) -> {error, no_master}. %%----------------------------------------------------------------- %% Write start_erl.data. %%----------------------------------------------------------------- write_start(File, Data, false) -> case do_write_file(File, Data) of ok -> ok; Error -> throw(Error) end; write_start(File, Data, Masters) -> all_masters(Masters), write_start_m(File, Data, Masters). %%----------------------------------------------------------------- %% Write the "start_erl.data" file at all master nodes. %% 1. Save "start_erl.backup" at all nodes. %% 2. Write the "start_erl.change" file at all nodes. %% 3. Move "start_erl.change" to "start_erl.data". %% 4. Remove "start_erl.backup" at all nodes. %% %% If one of the steps above fails, all steps is recovered from %% (as long as possible), except for 4 which is allowed to fail. %%----------------------------------------------------------------- write_start_m(File, Data, Masters) -> Dir = filename:dirname(File), Backup = filename:join(Dir, "start_erl.backup"), Change = filename:join(Dir, "start_erl.change"), case at_all_masters(Masters, ?MODULE, do_copy_files, [File, [Backup]]) of ok -> case at_all_masters(Masters, ?MODULE, do_write_file, [Change, Data]) of ok -> case at_all_masters(Masters, file, rename, [Change, File]) of ok -> remove_files(all, [Backup, Change], Masters), ok; {error, {Master, R}} -> takewhile(Master, Masters, file, rename, [Backup, File]), remove_files(all, [Backup, Change], Masters), throw({error, {Master, R, move_start_erl}}) end; {error, {Master, R}} -> remove_files(all, [Backup, Change], Masters), throw({error, {Master, R, write_start_erl}}) end; {error, {Master, R}} -> remove_files(Master, [Backup], Masters), throw({error, {Master, R, backup_start_erl}}) end. %%----------------------------------------------------------------- %% Copy the "start.boot" and "sys.config" from SrcDir to DestDir at all %% master nodes. %% 1. Save DestDir/"start.backup" and DestDir/"sys.backup" at all nodes. %% 2. Copy files at all nodes. %% 3. Remove backup files at all nodes. %% %% If one of the steps above fails, all steps is recovered from %% (as long as possible), except for 3 which is allowed to fail. %%----------------------------------------------------------------- set_static_files(SrcDir, DestDir, Masters) -> all_masters(Masters), Boot = "start.boot", Config = "sys.config", SrcBoot = filename:join(SrcDir, Boot), DestBoot = filename:join(DestDir, Boot), BackupBoot = filename:join(DestDir, "start.backup"), SrcConf = filename:join(SrcDir, Config), DestConf = filename:join(DestDir, Config), BackupConf = filename:join(DestDir, "sys.backup"), case at_all_masters(Masters, ?MODULE, do_copy_files, [[{DestBoot, BackupBoot}, {DestConf, BackupConf}]]) of ok -> case at_all_masters(Masters, ?MODULE, do_copy_files, [[{SrcBoot, DestBoot}, {SrcConf, DestConf}]]) of ok -> remove_files(all, [BackupBoot, BackupConf], Masters), ok; {error, {Master, R}} -> takewhile(Master, Masters, ?MODULE, do_rename_files, [{BackupBoot, DestBoot}, {BackupConf, DestConf}]), remove_files(all, [BackupBoot, BackupConf], Masters), throw({error, {Master, R, copy_start_config}}) end; {error, {Master, R}} -> remove_files(Master, [BackupBoot, BackupConf], Masters), throw({error, {Master, R, backup_start_config}}) end. From luc.taesch@REDACTED Wed Oct 11 17:26:36 2000 From: luc.taesch@REDACTED (luc.taesch@REDACTED) Date: Wed, 11 Oct 2000 17:26:36 +0200 Subject: compiler question ? Message-ID: is the compiler smart enough to detect that something is unchanged, and then not copy it again ( on stacks, or whatever ) f(A) ->2* g(A). is A copied on the stack, or just passed around ? ( Ais big in my case, and quite deep recursion is happenning) generally speaking , in FP , are things passed around as values or pointer ? From bjorn@REDACTED Wed Oct 11 17:42:49 2000 From: bjorn@REDACTED (Bjorn Gustavsson) Date: 11 Oct 2000 17:42:49 +0200 Subject: compiler question ? In-Reply-To: luc.taesch@ubs.com's message of "Wed, 11 Oct 2000 17:26:36 +0200" References: Message-ID: luc.taesch@REDACTED writes: > is the compiler smart enough to detect that something is unchanged, and then > not copy it again ( on stacks, or whatever ) > > f(A) ->2* g(A). > > is A copied on the stack, or just passed around ? ( Ais big in my case, and > quite deep recursion is happenning) The Beam compiler only saves variables on the stack if they will actually be used later in the same function. In your case, A will not be saved on the stack. > > generally speaking , in FP , are things passed around as values or pointer ? > It depends on the type. In Erlang we use 32 bits words that can contain either a value (for instance an integer or an atom), or pointer to complex terms likes list cells or tuples. In both cases there a tag bits that indicates the type. /Bjorn -- Bj?rn Gustavsson Ericsson Utvecklings AB bjorn@REDACTED ?T2/UAB/F/P BOX 1505 +46 8 727 56 87 125 25 ?lvsj? From luc.taesch@REDACTED Wed Oct 11 17:54:31 2000 From: luc.taesch@REDACTED (luc.taesch@REDACTED) Date: Wed, 11 Oct 2000 17:54:31 +0200 Subject: compiler question ? Message-ID: cool.and what about lists or tuples? f({A,B})->{g(A),B}. is B copied ? g([H?T])-> [k(H) ? T]. is T copied ? ( forget about tail recursiveness, can unmodified part of list be not copied ? > -----Original Message----- > From: bjorn [mailto:bjorn@REDACTED] > Sent: Mittwoch, 11. Oktober 2000 17:43 > To: Taesch, Luc > Cc: erlang-questions; bjorn > Subject: Re: compiler question ? > > > luc.taesch@REDACTED writes: > > > is the compiler smart enough to detect that something is > unchanged, and then > > not copy it again ( on stacks, or whatever ) > > > > f(A) ->2* g(A). > > > > is A copied on the stack, or just passed around ? ( Ais big > in my case, and > > quite deep recursion is happenning) > > The Beam compiler only saves variables on the stack if they > will actually > be used later in the same function. In your case, A will not > be saved on > the stack. > > > > > generally speaking , in FP , are things passed around as > values or pointer ? > > > > It depends on the type. In Erlang we use 32 bits words that > can contain > either a value (for instance an integer or an atom), or > pointer to complex > terms likes list cells or tuples. In both cases there a tag bits that > indicates the type. > > /Bjorn > -- > Bj?rn Gustavsson Ericsson Utvecklings AB > bjorn@REDACTED ?T2/UAB/F/P > BOX 1505 > +46 8 727 56 87 125 25 ?lvsj? > From peter@REDACTED Wed Oct 11 19:32:36 2000 From: peter@REDACTED (Peter H|gfeldt) Date: Wed, 11 Oct 2000 19:32:36 +0200 (MET DST) Subject: release handling In-Reply-To: <402DD461F109D411977E0008C791C3125651D4@imp02mbx.one2one.co.uk> Message-ID: I wrote a utility called `uptest', the purpose of which is to test upgrade and downgrade of one single application. The members of the OTP development team use it internally. It is not open source (yet). The attached document outlines its implementation, which reveales some of the quirks of systools and release_handler, in particular the question of creating the `initial' release. /Peter PS. Don't bother about missing *.gif files. DS. ------------------------------------------------------------------------- Peter H?gfeldt e-mail : peter@REDACTED Open Telecom Platform Phone: : +46 (8) 727 57 58 Ericsson Utvecklings AB Mobile : +46 070-519 57 51 S-126 25 STOCKHOLM Fax: : +46 (8) 727 5775 Office address: Armborstv?gen 1, ?lvsj? -------------- next part -------------- Uptest Implementation Ericsson Blue Logotype
Prepared
Peter Högfeldt
No.
Approved
Checked
Date
2000-02-09
Rev
PA2
File
uptest_implementation.sgml

Uptest Implementation

1 Introduction

The purpose of the uptest utility is to simplify testing of code replacement for Erlang applications.

There are currently the following limitations:

  • Erlang emulator version cannot be replaced,

  • only one application at a time can be tested.

2 Work Phases

Code replacement for an application can be tested by using the uptest utility.There are two phases.

The first phase consists of evaluating in any Erlang emulator the following functions (in the order given).

config()
Prompts the user for configuration parameters, for instance the location of the Erlang emulator to use when testing code replacement; the locations of the old and new versions of the application to test.
check()
Checks the consistency of contents of ebin directories, .app files, and .appup file.
setup()
Here two releases are prepared: base and top. Boot scripts, .rel files, and a relup file are created, and emulator executables and application code is put in a test directory (specified in config()).

In the second phase the emulator in the test directory (created by setup()) is started, including the application to test, and the following two functions can be evaluated:

upgrade()
Upgrades the application from the base version to the top version.
downgrade()
Downgrades the application from the top version to the base version.

Both upgrade() and downgrade() checks the supervision tree of the application before and after code replacement, as well as the versions of replaced modules.

3 Details

3.1 config

The user is prompted for the following information:

  1. an Erlang/OTP root directory,

  2. the version of the Erlang runtime system to use,

  3. the location and version of the application to upgrade from (the base version),

  4. the location and version of the application to upgrade to (the top version),

  5. the location of the test directory where code replacement work shall be done.

Other applications needed by the tested application are (recursively) found, including also kernel, stdlib, sasl and uptest (which is later made into an application of its own).

All information is written to the file uptest.conf in the current directory.

The following is an example configuration, which we will refer to later on.

1. The application to test is pea, with versions 1.0 and 1.1, located in /home/nisse/pea_build/lib/pea-1.0 and /home/nisse/pea_build/lib/pea-1.1 respectivly.

2. The Erlang root directory is /usr/local/otp/r6b from which the emulator of version 4.9.1 will be picked.

3. The test directory is /home/nisse/pea_test/TEST.

4. Other applications needed are kernel-2.4.3, stdlib-1.8.2, sasl-1.8.2 and uptest-1.1 (yes, uptest will included as an application in the releases built in setup below).

3.2 check

Here the following things are checked for both the base version and the top version of the application:

1. that the set of modules in the ebin directory, and the set of modules in the .app do not differ (if they do a warning is displayed),

2. that the set of added/removed modules according to the .app file does not differ from that of the .appup file,

3. by comparing module versions of the base and top version of the application, detecting (i) those modules that are changed according to the .appup file, but have the same version, (ii) modules that have different versions, but are not listed in the .appup file (if there are such modules a warning is displayed).

3.3 setup

This is the most elaborate function. We consider it in two parts.

Note: Some intermediate files are created in the current directory.

3.3.1 The base release

1. A base.rel is created in the current directory:

     {release,{"TEST","1.0"},
          {erts,"4.9.1"},
          [{kernel,"2.4.3"},
          {stdlib,"1.8.2"},
          {sasl,"1.8.2"},
          {uptest,"1.1"},
          {pea,"1.0"}]}.

        

2. The base.script and base.boot files are created in the current directory by a call

     systools:make_script("base", [{path, AppEbinDirs}]),
        

where AppEbinDirs is a list of paths to all the applications involved, so that the .app files can be found and be incorporated in the boot script.

3. We also create a plain.rel file, and the corresponding plain.script and plain.boot files (in the current directory). The plain.rel file only specifies the kernel and stdlib applications, so that the plain.boot file can be used to start the system without starting the application pea, which might be needed if it has to be configured before it can be properly started.

4. A tar file base.tar.gz is created in the current directory by the call

     systools:make_tar("base", [{path, AppEbinDirs}, {erts, RootDir}]), 
        

where AppEbinDirs is as above, and RootDir is the Erlang root directory (in the example configuration it is /usr/local/otp/r6b). The tar file now contains the following directories and files:

     lib/kernel-2.4.3/...
          ...
          lib/pea-1.0/...
          releases/base.rel
          releases/1.0/start.boot
          erts-4.9.1/...
        

The start.boot is a copy of the base.boot file in the current directory, and base.rel is the same as base.rel in the current directory.

Note: Believe it or not, but systools:make_tar/2 tacitely incorporates into the tar file the "system files" existing in the current directory according to the following table (see the function add_system_files/4 in the module systools_make). Here in our example configuration case RelFileName = "base", and SVsn = "1.0".

File in current directory Destination in tar file
RelFileName.boot releases/SVsn/start.boot
relup releases/SVsn/relup
sys.config releases/SVsn/sys.config
RelFileName.rel releases/RelFileName.rel

5. The tar file base.tar.gz in the current directory is now extracted into the test directory by the call

     erl_tar:extract("base.tar.gz", [keep_old_files, {cwd, TestRoot}, 
          compressed]),
        

where TestRoot is the test directory (in our example is is bound to /home/nisse/pea_test/TEST).

In the test directory we now have the following directories:

     lib/kernel-2.4.3/...
          ...
          lib/pea-1.0/...
          releases/base.rel
          releases/1.0/start.boot
          erts-4.9.1/...
        

6. Within the test directory we create the file erts-4.9.1/bin/erl by substituting the string %FINAL_ROOTDIR% for TestRoot in the file erts-4.9.1/bin/erl.src.

7. The directory bin is created within the test directory, and the following files are copied within the same directory:

Source Destination
erts-4.9.1/bin/erl bin/erl
erts-4.9.1/bin/erlc bin/erlc
erts-4.9.1/bin/epmd bin/epmd

8. From the current directory the following files are copied into the test directory

File in current directory Destination in test directory
base.script bin/base.script
base.boot bin/base.boot
plain.script bin/plain.script
plain.boot bin/plain.boot

9. In the test directory the file releases/RELEASES is created by the call

     release_handler:create_RELEASES(TestRoot, "base.rel"),
        

where TestRoot is as above.

10. Within the test directory the file releases/start_erl.data is created the contents of which is the erts version and the release version - in our example configuration one line containing

     "4.9.1 1.0"
        

11. In the current directory the file start_erl is created, containing the following shell script (this is for Unix only):

          #!/bin/sh
          #
          read emu rel < %TEST%/releases/start_erl.data
          exec %TEST%/bin/erl \
          -boot %TEST%/releases/$rel/start \
          -uptest workdir %WORKDIR% \
          $*
        

where %TEST% is substituted for the test directory, and %WORKDIR% for the current directory.

The purpose of this shell script is to start the system in "code replacement mode".

12. In the current directory the file start_plain is created, containing the following shell script (this is for Unix only):

          #!/bin/sh
          #
          exec %TEST%/bin/erl -boot %TEST%/bin/plain $*
        

The purpose of this shell script is to be able to start a minimal system in order to configure the application that is tested, before it is really started.

3.3.2 The top release

The top release is created similarly to the base release. However, there are some differences. XXX What differences?

1. A top.rel is created in the current directory:

     {release,{"TEST","1.1"},
          {erts,"4.9.1"},
          [{kernel,"2.4.3"},
          {stdlib,"1.8.2"},
          {sasl,"1.8.2"},
          {uptest,"1.1"},
          {pea,"1.1"}]}.

        

2. The top.script and top.boot files are created in the current directory by a call

     systools:make_script("top", [{path, AppEbinDirs}]),
        

where AppEbinDirs is a list of paths to all the applications involved, so that the .app files can be found and be incorporated in the boot script.

3. A relup file is created in the current directory by a call to

     systools:make_relup("top", ["base"], ["base"], 
                              [{path, AppBaseEbinDirs ++ AppTopEbinDirs}]),
        

where AppBaseEbinDirs and AppTopEbinDirs are lists paths to ebin directories of the applications in the base and top applications, respectively.

4. A tar file top.tar.gz is created in the current directory by the call

     systools:make_tar("top", [{path, AppTopEbinDirs}]), 
        

where AppTopEbinDirs is as above, and RootDir is the Erlang root directory (in the example configuration it is /usr/local/otp/r6b). The tar file now contains the following directories and files:

     lib/kernel-2.4.3/...
          ...
          lib/pea-1.1/...
          releases/top.rel
          releases/1.0/relup
          releases/1.0/start.boot
        

The start.boot is a copy of the top.boot file in the current directory, and top.rel is the same as top.rel in the current directory.

Note that the tar file does not contain an erts directory.

5. The tar file top.tar.gz in the current directory is copied to the releases directory within the test directory.

3.4 upgrade

The system is started by calling

   ./start_erl
      

in the current directory.

1. The top release is unpacked by a call to

   release_handler:unpack_release("top").
      

that returns a release version Vsn = "1.0".

2. The top release is installed by a call to

   release_handler:install_release(Vsn).
      

that performs code replacement for upgrading.

3. Next the release is made permanent by the call

   release_handler:make_permanent(Vsn)
      

that changes the contents of releases/RELEASES and releases/start_erl.data.

3.5 downgrade

Similar to upgrade.


Copyright © 1991-2000 Ericsson Utvecklings AB
From kent@REDACTED Thu Oct 12 00:40:12 2000 From: kent@REDACTED (Kent Boortz) Date: 12 Oct 2000 00:40:12 +0200 Subject: compiler question ? In-Reply-To: luc.taesch@ubs.com's message of "Wed, 11 Oct 2000 17:54:31 +0200" References: Message-ID: > cool.and what about lists or tuples? > > f({A,B})->{g(A),B}. is B copied ? > > g([H?T])-> [k(H) ? T]. is T copied ? ( forget about tail recursiveness, can > unmodified part of list be not copied ? You can check this yourself, % erlc -S test.erl produces a file "test.S" that is not that hard to read (I think some optimizations are done at load time but the code is close to what is executed). In the *.S file the "allocate" and "deallocate" makes space on the stack, a 'y' variable is located on the stack. From this we can see that 'B' is stored on stack as well as 'T'. When you write "copied" I assume you mean one cell (32 bits) needed for storing the object or a pointer to a larger object on the stack. The object itself is not copied in the examples above. It is rare that Erlang data is copied, the main occasions are when sending messages or storing data into ETS tables. But there are still some optimizations missing in the compiler that cause some "copying". It will not transform constructs consuming heap space like h({A,B}) -> k({A,B}). to something equivalent that is not using heap space h({A,B} = X) -> k(X). kent From vances@REDACTED Thu Oct 12 04:11:53 2000 From: vances@REDACTED (Vance Shipley) Date: Wed, 11 Oct 2000 22:11:53 -0400 Subject: erl_call Message-ID: Why is it that erl_call does not get installed? Is this tool not supported any longer? The reason I want it is that it is suggested in the Embedded Users Guide that it can be used in an rc script to effect a graceful shutdown. I would am putting it in so that when the Unix system is shutdown Erlang/OTP will get a clean signal to terminate properly. erl_call is getting built, and I can use it, it just doesn't get installed which makes me wonder ... -Vance From kenneth@REDACTED Thu Oct 12 09:58:49 2000 From: kenneth@REDACTED (Kenneth Lundin) Date: Thu, 12 Oct 2000 09:58:49 +0200 Subject: erl_call References: Message-ID: <39E56F39.6AE0582C@erix.ericsson.se> Vance Shipley wrote: > > Why is it that erl_call does not get installed? > Is this tool not supported any longer? > > The reason I want it is that it is suggested in the > Embedded Users Guide that it can be used in an rc > script to effect a graceful shutdown. I would am > putting it in so that when the Unix system is shutdown > Erlang/OTP will get a clean signal to terminate > properly. > > erl_call is getting built, and I can use it, it just > doesn't get installed which makes me wonder ... > > -Vance erl_call is distributed as an example of what you can do with the erl_interfac library, it is part of the erl_interface application and is thus located under ERL_ROOT/lib/erl_interface-3.2.3/bin. When and if there is a new version of erl_interface there will also be a new version of erl_call. Previously erl_call and the erl_interface libraries where installed in central places directly under ERL_ROOT which made it problematic to have 2 versions installed at the same time and letting the user choose between the versions. We have made the decision that it is better to keep all files belonging to an application together as much as possible. Of course there can be improvements in what the installation does, using symbolic links etc. but we don't regard erl_call as that important. regards Kenneth -- Kenneth Lundin Ericsson Utvecklings AB kenneth@REDACTED ?T2/UAB/F/P BOX 1505 +46 8 727 57 25 125 25 ?lvsj? From rv@REDACTED Thu Oct 12 10:44:53 2000 From: rv@REDACTED (Robert Virding) Date: Thu, 12 Oct 2000 10:44:53 +0200 Subject: compiler question ? In-Reply-To: Your message of "12 Oct 2000 00:40:12 +0200." Message-ID: <200010120844.KAA05322@trana.bluetail.com> Kent Boortz writes: > >> cool.and what about lists or tuples? >> >> f({A,B})->{g(A),B}. is B copied ? >> >> g([H?T])-> [k(H) ? T]. is T copied ? ( forget about tail recursiveness, can >> unmodified part of list be not copied ? > >When you write "copied" I assume you mean one cell (32 bits) needed >for storing the object or a pointer to a larger object on the >stack. The object itself is not copied in the examples above. It is >rare that Erlang data is copied, the main occasions are when sending >messages or storing data into ETS tables. I think he meant the the whole object. One of the beauties of having non-destructive semantics is that you can quite safely send around references to objects, which is what Erlang implementations do. This means that you never copy more than 32-bit references. >But there are still some optimizations missing in the compiler that >cause some "copying". It will not transform constructs consuming heap >space like > > h({A,B}) -> > k({A,B}). > >to something equivalent that is not using heap space > > h({A,B} = X) -> > k(X). It is not at all certain that this is a good optimisation. While it may use less heap space it may also carry some penalties: 1. It may mean that more objects become old before being dereferenced which is not so good with generational collectors, they love objects dying young. 2. It may use more stack space as it will keep more references. If anyone has some references to tests on this optimisation please send them to me. Robert From mickael.remond@REDACTED Thu Oct 12 14:22:55 2000 From: mickael.remond@REDACTED (Mickael Remond) Date: 12 Oct 2000 14:22:55 +0200 Subject: Passing argument from the command line Message-ID: <873di29rkw.fsf@western.ird.idealx.com> It seems that the argument of the Erlang command line are always list of atoms... Is it right or am I missing something ? In fact, whith this fact you need to design your functions specifically if you want them to be called directly from the command line and from the Erlang shell. For example: I was building a makefile to transform .ehtml into .html files. The command I am using is: erl -s ehtml file index -s init stop And it does not work from the command line. I have changed the following code in ehtml.erl: file(F) when list(F) -> file(F ++ ".ehtml", F ++ ".html"). into: % Called from the command line file([F|Empty]) when atom(F), Empty==[] -> file(atom_to_list(F)); % Called from the Erlang Shell file(F) when list(F) -> file(F ++ ".ehtml", F ++ ".html"). And the command line is working. Am I missing something or must I make sure that all function that need to be command line compliant should accept list of atoms ? Thank you for help. -- Micka?l R?mond From richardc@REDACTED Thu Oct 12 14:55:41 2000 From: richardc@REDACTED (Richard Carlsson) Date: Thu, 12 Oct 2000 14:55:41 +0200 (MET DST) Subject: Erlang parse trees In-Reply-To: <873di29rkw.fsf@western.ird.idealx.com> Message-ID: Just a little announcment: Every now and then, I see people asking for documentation on the data structure (the "parse trees") that the Erlang standard library parser constructs. However, that structure is still getting changed as needed, and is not thoroughly documented anywhere. Also, it is really pretty hairy to work with. Whether you want to analyze, manipulate, or merely pretty-print Erlang source code, I suggest you try the abstract datatype that I have written in order to make these things easier and also to protect programs from changes in the parser data structures. As well as being backwards-compatible with the output from `erl_parse', it also allows you to do a lot of nifty things like attaching source-code comments and user annotations to nodes in the syntax trees. http://www.erlang.org/user.html#syntax_tools-1.0 The package also contains a completely new and very flexible pretty-printer that can be used directly on any `erl_parse' output without any knowledge of how the abstract datatype works, and also modules that can extract comment lines from Erlang source code and automatically insert them into the syntax tree at the right places (works like magic). I'd like to get some users of this package - it has been pretty thoroughly tested, so I don't expect many bug reports, but any feedback would be appreciated. /Richard Carlsson Richard Carlsson (richardc@REDACTED) (This space intentionally left blank.) E-mail: Richard.Carlsson@REDACTED WWW: http://www.csd.uu.se/~richardc/ From Sean.Hinde@REDACTED Thu Oct 12 16:21:49 2000 From: Sean.Hinde@REDACTED (Sean Hinde) Date: Thu, 12 Oct 2000 15:21:49 +0100 Subject: Who is a Programmer? Message-ID: <402DD461F109D411977E0008C791C3125651E2@imp02mbx.one2one.co.uk> Hi all, After my statement at the start of the EUC that I was "not a programmer" I've been thinking some more about what I actually meant.. which I'd like to share... I think what I mean is that I am not a programmer in the same way that an accountant who writes clever macros in Excel is not a programmer. Before Excel this person would have relied on a team of programmers to create a monolithic accounting system which did everything, and used a desktop calculator for local work. After the invention of Excel the Accountant had a tool which he/she could use locally to implement what had previously been the exclusive preserve of professional programmers. This tendency has lurched forwards (and sometimes backwards - C++) towards reducing the skill level to the point where the end user can write their own "programs" without being a programmer (Dept Administrator produces Web pages) This has also relied on the increase in processing power available - we used to have an Excel macro to design a GSM traffic matrix which took 12 hours to run on a 386 - we needed a programmer to turn this into a C program to make it faster. I'm not sure this C program is used anymore - there is just an 800MHz Pentium in the corner running Excel My job and core skill is as a designer of telecoms services, which basically means I need to understand a whole bunch of signalling protocols and know how they can be used to do new whizzy stuff in the GSM network. My first step in designing a service is to work out what changes/new systems/functionality are required to implement the service. In the past, like the accountant I would have potentially needed to have a team of professional programmers to deliver a new platform - normally outsourced to some software house (maybe even Ericsson :-)). I see Erlang/OTP as a step towards having the Excel of my trade - allowing me to implement telecoms logic and databases in a safe and simple way as an "end user". In other words the difference between the spec and the Erlang program is so small that it is easier and quicker to do it myself than to explain it and get someone else to do it. This doesn't mean I am a "non programmer" i.e. someone who doesn't and can't program - The two things are not the same. It also doesn't mean that I don't enormously value all the true programming which has gone into OTP, and the contributions of others who are professional programmers (e.g. locker - thanks Ulf). I wonder where it will all end Sean NOTICE AND DISCLAIMER: This email (including attachments) is confidential. If you have received this email in error please notify the sender immediately and delete this email from your system without copying or disseminating it or placing any reliance upon its contents. We cannot accept liability for any breaches of confidence arising through use of email. Any opinions expressed in this email (including attachments) are those of the author and do not necessarily reflect our opinions. We will not accept responsibility for any commitments made by our employees outside the scope of our business. We do not warrant the accuracy or completeness of such information. From luc.taesch@REDACTED Thu Oct 12 17:02:27 2000 From: luc.taesch@REDACTED (luc.taesch@REDACTED) Date: Thu, 12 Oct 2000 17:02:27 +0200 Subject: Who is a Programmer? Message-ID: interesting. got similar thinking over time, unorganised. erlang provides "executable "spec. u still have to spec. the neeeded skills became then ; - detailed knowldge of business: the former analyst, which can turn detailed knowledge into detailed spec . - architect, that provide "structure" for the detailled knowledge to integrate into: -business architect, that design an overall business requirement (vision) into a "suite of component" (smaller boxes) : super-structure . -technical architect, that design the overall technical requirement the detailled specs will fit into : infra-structure, - some process management to make sure the boxes fit into each other. of course, otp is a pre-made architecture for telecom infra-structure. ----------------------------------- Hi all, After my statement at the start of the EUC that I was "not a programmer" I've been thinking some more about what I actually meant.. which I'd like to share... I think what I mean is that I am not a programmer in the same way that an accountant who writes clever macros in Excel is not a programmer. Before Excel this person would have relied on a team of programmers to create a monolithic accounting system which did everything, and used a desktop calculator for local work. After the invention of Excel the Accountant had a tool which he/she could use locally to implement what had previously been the exclusive preserve of professional programmers. This tendency has lurched forwards (and sometimes backwards - C++) towards reducing the skill level to the point where the end user can write their own "programs" without being a programmer (Dept Administrator produces Web pages) This has also relied on the increase in processing power available - we used to have an Excel macro to design a GSM traffic matrix which took 12 hours to run on a 386 - we needed a programmer to turn this into a C program to make it faster. I'm not sure this C program is used anymore - there is just an 800MHz Pentium in the corner running Excel My job and core skill is as a designer of telecoms services, which basically means I need to understand a whole bunch of signalling protocols and know how they can be used to do new whizzy stuff in the GSM network. My first step in designing a service is to work out what changes/new systems/functionality are required to implement the service. In the past, like the accountant I would have potentially needed to have a team of professional programmers to deliver a new platform - normally outsourced to some software house (maybe even Ericsson :-)). I see Erlang/OTP as a step towards having the Excel of my trade - allowing me to implement telecoms logic and databases in a safe and simple way as an "end user". In other words the difference between the spec and the Erlang program is so small that it is easier and quicker to do it myself than to explain it and get someone else to do it. This doesn't mean I am a "non programmer" i.e. someone who doesn't and can't program - The two things are not the same. It also doesn't mean that I don't enormously value all the true programming which has gone into OTP, and the contributions of others who are professional programmers (e.g. locker - thanks Ulf). I wonder where it will all end Sean From Gerald.Biederbeck@REDACTED Thu Oct 12 17:25:01 2000 From: Gerald.Biederbeck@REDACTED (Gerald Biederbeck) Date: Thu, 12 Oct 2000 17:25:01 +0200 Subject: .erlang file on windows Message-ID: <39E5D7CD.ADDC4082@eede.ericsson.se> Hi, I would like to - load/compile some modules - change directories - start a certain erlang module and so on... AUTOMATICALLY during startup of the Erlang-Shell! On a UNIX/LINUX machine there is this '.erlang' file -> no problem! But how is that achieved on a Windows9x machine? Do I really have to create a '.erlang' file on a LINUX machine and copy it to the Windows9x machine afterwards??? Isn't there a smarter way? Did I miss something in the Erlang documentation? Thanx for any reply! Regards /Gerald B. From etxuwig@REDACTED Thu Oct 12 17:37:43 2000 From: etxuwig@REDACTED (Ulf Wiger) Date: Thu, 12 Oct 2000 17:37:43 +0200 (MET DST) Subject: Who is a Programmer? In-Reply-To: <402DD461F109D411977E0008C791C3125651E2@imp02mbx.one2one.co.uk> Message-ID: On Thu, 12 Oct 2000, Sean Hinde wrote: >I see Erlang/OTP as a step towards having the Excel of my trade - >allowing me to implement telecoms logic and databases in a safe and >simple way as an "end user". This is in line with Mike W's idea of Erlang as "an executable modelling language". Funny - one of the camps resisting Erlang has been those trying to move development more towards high-level graphical modelling tools like Rational Rose, possibly thinking that this is the way out of the Software Crisis. To them (I imagine) Erlang represents a hacker culture; to you (and me), Erlang is a way for us system architects to become the programming gurus we never thought we'd be. (: /Uffe -- Ulf Wiger tfn: +46 8 719 81 95 Network Architecture & Product Strategies mob: +46 70 519 81 95 Ericsson Telecom AB, Datacom Networks and IP Services Varuv?gen 9, ?lvsj?, S-126 25 Stockholm, Sweden From luc.taesch@REDACTED Thu Oct 12 17:55:56 2000 From: luc.taesch@REDACTED (luc.taesch@REDACTED) Date: Thu, 12 Oct 2000 17:55:56 +0200 Subject: Who is a Programmer? Message-ID: >Funny - one of the camps resisting Erlang has been those trying to >move development more towards high-level graphical modelling tools >like Rational Rose, possibly thinking that this is the way out of the >Software Crisis. ive been a long time trier, supporter, implementer... of graphical modelling tools, oo, and pre-oo. never "really" works. if i have to explain it in a nutshell, i d say "imagine we stayed with hieroglyphs...instead of letters and words", too many picture, not enough expressing power by combination. kind of similar to what u say following : >To them (I imagine) Erlang represents a hacker >culture; to you (and me), Erlang is a way for us system architects >to become the programming gurus we never thought we'd be. formal specs, Z like, looking like complex math, are frightening to look at (until ure used to, but how many passes this ? try with ure manager...) erlang syntax brings it close to common usage. (haskell is already too cryptic, with points, colon, and so on) From vances@REDACTED Thu Oct 12 17:54:30 2000 From: vances@REDACTED (Vance Shipley) Date: Thu, 12 Oct 2000 11:54:30 -0400 Subject: Embedded system on Linux Message-ID: Currently embedded systems are only built for Solaris. This is too strict for our liking here. In fact we have built and used embedded Erlang/OTP systems on both Unixware and Linux. Below is a patch for R7B to get an embedded system built and working under Linux (at least RedHat 6.2). -Vance --- erts/etc/common/Makefile.in.ORIG Tue Nov 23 10:04:36 1999 +++ erts/etc/common/Makefile.in Wed Oct 11 19:59:09 2000 @@ -72,6 +72,11 @@ INSTALL_EMBEDDED_DATA = ../unix/start.src ../unix/start_erl.src endif +ifeq ($(findstring linux,$(TARGET)), linux) +INSTALL_EMBEDDED_PROGS = $(BINDIR)/run_erl $(BINDIR)/to_erl +INSTALL_EMBEDDED_DATA = ../unix/start.src ../unix/start_erl.src +endif + ifeq ($(findstring vxworks,$(TARGET)), vxworks) INSTALL_EMBEDDED_PROGS = $(BINDIR)/erl_io $(BINDIR)/rdate $(BINDIR)/vxcall INSTALL_EMBEDDED_DATA = $(BINDIR)/erl_script.sam $(VXETC)/resolv.conf --- erts/etc/unix/Install.src.ORIG Tue Aug 22 08:15:23 2000 +++ erts/etc/unix/Install.src Wed Oct 11 20:07:38 2000 @@ -64,9 +64,9 @@ # # Create start file for embedded system use, -# currently only on Solaris/SunOS 5 +# currently only on Solaris/SunOS 5 & Linux # -if [ "X$TARGET" = "Xsunos5" ]; then +if [ "X$TARGET" = "Xsunos5" -o "X$TARGET" = "Xlinux" ]; then (cd $ERL_ROOT/erts-%I_VSN%/bin; sed -e "s;%FINAL_ROOTDIR%;$ERL_ROOT;" start.src > start; chmod 755 start) From Sean.Hinde@REDACTED Thu Oct 12 19:26:24 2000 From: Sean.Hinde@REDACTED (Sean Hinde) Date: Thu, 12 Oct 2000 18:26:24 +0100 Subject: .erlang file on windows Message-ID: <402DD461F109D411977E0008C791C3125651E3@imp02mbx.one2one.co.uk> I guess you need to get a decent editor for windows which allows you to call files by the name you give them - I use XEmacs for NT on my laptop. www.xemacs.org I'm sure I read somewhere that emacs provides an alternative filename for its .emacs file for MS machines but I can't find the reference - either way it would be a friendly thing for Erlang/OTP Rgds, Sean > -----Original Message----- > From: Gerald Biederbeck [mailto:Gerald.Biederbeck@REDACTED] > Sent: 12 October 2000 16:25 > To: erlang-questions@REDACTED > Subject: .erlang file on windows > > > Hi, > > I would like to > - load/compile some modules > - change directories > - start a certain erlang module > and so on... > AUTOMATICALLY during startup of the Erlang-Shell! > On a UNIX/LINUX machine there is this '.erlang' file -> no problem! > > But how is that achieved on a Windows9x machine? > Do I really have to create a '.erlang' file on a LINUX machine > and copy it to the Windows9x machine afterwards??? > > Isn't there a smarter way? > Did I miss something in the Erlang documentation? > > Thanx for any reply! > > Regards > /Gerald B. > NOTICE AND DISCLAIMER: This email (including attachments) is confidential. If you have received this email in error please notify the sender immediately and delete this email from your system without copying or disseminating it or placing any reliance upon its contents. We cannot accept liability for any breaches of confidence arising through use of email. Any opinions expressed in this email (including attachments) are those of the author and do not necessarily reflect our opinions. We will not accept responsibility for any commitments made by our employees outside the scope of our business. We do not warrant the accuracy or completeness of such information. From aba3600@REDACTED Thu Oct 12 19:52:27 2000 From: aba3600@REDACTED (Andy with Recycled Electrons) Date: Thu, 12 Oct 2000 12:52:27 -0500 (CDT) Subject: Who is a Programmer? In-Reply-To: <402DD461F109D411977E0008C791C3125651E2@imp02mbx.one2one.co.uk> Message-ID: > This tendency has lurched forwards (and sometimes backwards - C++) towards comp.lang.cpp.sucks.sucks.sucks.sucks a.k.a. alt.strange.electronics.weird.erlang (no, these are not real groups as far as I know) > In other words the difference between the spec and the Erlang program is so > small that it is easier and quicker to do it myself than to explain it and > get someone else to do it. That means that English is less suited to describing the problem than Erlang ;o) > I wonder where it will all end Do you remember "Q" on Star Trek, The Next Generation? He'll be carrying a Palm 3000 running a BEAM emulator. Sincerely, Andy Allen andy@REDACTED From crd@REDACTED Thu Oct 12 19:42:34 2000 From: crd@REDACTED (Craig Dickson) Date: Thu, 12 Oct 2000 10:42:34 -0700 Subject: .erlang file on windows References: <39E5D7CD.ADDC4082@eede.ericsson.se> Message-ID: <00c901c03473$ceb00760$4d01140a@inkpad> Why should you have to create a .erlang file on *nix and copy it over? Just create it on the Windows machine. I realize that the Windows Explorer won't let you create a filename beginning with a dot, but the shell window will. Craig ----- Original Message ----- From: "Gerald Biederbeck" To: Sent: Thursday, 12 October 2000 08:25 am Subject: .erlang file on windows > Hi, > > I would like to > - load/compile some modules > - change directories > - start a certain erlang module > and so on... > AUTOMATICALLY during startup of the Erlang-Shell! > On a UNIX/LINUX machine there is this '.erlang' file -> no problem! > > But how is that achieved on a Windows9x machine? > Do I really have to create a '.erlang' file on a LINUX machine > and copy it to the Windows9x machine afterwards??? > > Isn't there a smarter way? > Did I miss something in the Erlang documentation? > > Thanx for any reply! > > Regards > /Gerald B. > From luke@REDACTED Thu Oct 12 20:02:43 2000 From: luke@REDACTED (Luke Gorrie) Date: 12 Oct 2000 20:02:43 +0200 Subject: .erlang file on windows In-Reply-To: Sean Hinde's message of "Thu, 12 Oct 2000 18:26:24 +0100" References: <402DD461F109D411977E0008C791C3125651E3@imp02mbx.one2one.co.uk> Message-ID: Sean Hinde writes: > I guess you need to get a decent editor for windows which allows you to call > files by the name you give them - I use XEmacs for NT on my laptop. > www.xemacs.org > > I'm sure I read somewhere that emacs provides an alternative filename for > its .emacs file for MS machines but I can't find the reference - either way > it would be a friendly thing for Erlang/OTP I think you could achieve this manually with something like: erl -s file eval .. but I don't know how you'd write the path name on windows. -Luke From nsilva@REDACTED Fri Oct 13 17:23:58 2000 From: nsilva@REDACTED (Nuno Silva) Date: Fri, 13 Oct 2000 16:23:58 +0100 Subject: KQML parser Message-ID: Hi there, Does anyone know where can i find a KQML parser in Erlang? Does anyone thought about it?... Thanks, Nuno Silva ____________________________________________ Nuno Silva DEI - ISEP - IPP Rua Dr. Ant?nio Bernardino de Almeida 4200 - 072 Porto PORTUGAL Tel.: + 351 228340500 Fax: +351 228321159 e-mail: Nuno.Silva@REDACTED web: www.dei.isep.ipp.pt/~nsilva From andyfugard@REDACTED Fri Oct 13 20:02:30 2000 From: andyfugard@REDACTED (Andy Fugard) Date: Fri, 13 Oct 2000 19:02:30 +0100 Subject: .erlang file on windows References: <39E5D7CD.ADDC4082@eede.ericsson.se> <00c901c03473$ceb00760$4d01140a@inkpad> Message-ID: <003501c0353f$c235ee60$0300a8c0@toaster> > Why should you have to create a .erlang file on *nix and copy it over? Just > create it on the Windows machine. I realize that the Windows Explorer won't > let you create a filename beginning with a dot, but the shell window will. Yup - typing; notepad ".erlang" does the trick from a DOS prompt in a Win98 box, so it should be ok. From vladdu@REDACTED Sat Oct 14 00:30:04 2000 From: vladdu@REDACTED (Vlad Dumitrescu) Date: Sat, 14 Oct 2000 00:30:04 +0200 Subject: SAE References: Message-ID: Hi! It seems that there are no news about SAE in the latest release... Has anything more been done with it, or is Joe Armstrong's "how-to" at http://www.bluetail.com/~joe/sae/sae.html the latest development? I remember someone (Tony Rogvall?) mentioned something about fiddling with SAE in order to use shared libraries. Any progress? What about doing the same under Windows? The method used now won't work, as the "#!/bin/usr/beam_evm" header isn't Windows-ish... :-) regards, /Vlad From ltaesch@REDACTED Fri Oct 13 11:16:56 2000 From: ltaesch@REDACTED (Luc Taesch) Date: Fri, 13 Oct 2000 11:16:56 +0200 Subject: SAE References: Message-ID: <39E6D307.3677000D@europemail.com> Vlad Dumitrescu wrote: > Hi! > > It seems that there are no news about SAE in the latest release... Has anything more been done with it, or is Joe Armstrong's "how-to" at http://www.bluetail.com/~joe/sae/sae.html the latest development? > > I remember someone (Tony Rogvall?) mentioned something about fiddling with SAE in order to use shared libraries. Any progress? > > What about doing the same under Windows? The method used now won't work, as the "#!/bin/usr/beam_evm" header isn't Windows-ish... :-) > > regards, > /Vlad i havent been able to build it (r7b), so i would mind any help! (tried joes stuf, adapted to r7b-> [luc@REDACTED erts]$ make sae make[1]: Entering directory `/home/luc/tmp/otp_src_R7B-0/erts/sae' cd erts && ERL_TOP=/home/luc/tmp/otp_src_R7B-0 make NO_START_SCRIPTS=true opt /bin/sh: cd: erts: No such file or directory make[1]: *** [emulator] Error 1 make[1]: Leaving directory `/home/luc/tmp/otp_src_R7B-0/erts/sae' make: *** [sae] Error 2 [luc@REDACTED erts]$ make sae -n cd sae && make make[1]: Entering directory `/home/luc/tmp/otp_src_R7B-0/erts/sae' cd erts && ERL_TOP=/home/luc/tmp/otp_src_R7B-0 make NO_START_SCRIPTS=true opt /bin/sh: cd: erts: No such file or directory make[1]: *** [emulator] Error 1 make[1]: Leaving directory `/home/luc/tmp/otp_src_R7B-0/erts/sae' make: *** [sae] Error 2 [luc@REDACTED erts]$ -- First, they ignore you. Then, they laugh at you. Then, they fight you. Then, you win. --- Gandhi. Working code is what matter, not your market capitalization. --Kurt granroth -------------- next part -------------- An HTML attachment was scrubbed... URL: From vladdu@REDACTED Sat Oct 14 16:29:55 2000 From: vladdu@REDACTED (Vlad Dumitrescu) Date: Sat, 14 Oct 2000 16:29:55 +0200 Subject: what OS support is needed? Message-ID: Hi! me again with my strange questions... Lately I have looked quite intently at the QNX real time platform. It is a true micro-kernel architecture and most of the OS functionality is running as regular applications (it's really cool to be able to restart a driver without booting!) While waiting for the port (thanks, Sean - how can I help?), I am wondering what are the minimal OS capabilities needed for running a minimal Erlang? A memory manager is needed, a file system for the code module and a TCP/IP stack an associated stuff for communicating. But further than that? I also came to think that the file system is only needed in today's implementation. It is thinkable to have a more generic code module that will be able to load code from memory (in a ROMed system) for example, or from the resource pool of a Windows executable (aiding a lot the SAE thing, I think) regards, Vlad -------------- next part -------------- An HTML attachment was scrubbed... URL: From kent@REDACTED Sat Oct 14 17:39:34 2000 From: kent@REDACTED (Kent Boortz) Date: 14 Oct 2000 17:39:34 +0200 Subject: SAE In-Reply-To: Luc Taesch's message of "Fri, 13 Oct 2000 11:16:56 +0200" References: <39E6D307.3677000D@europemail.com> Message-ID: > i havent been able to build it (r7b), so i would mind any help! > (tried joes stuf, adapted to r7b-> > [luc@REDACTED erts]$ make sae > make[1]: Entering directory `/home/luc/tmp/otp_src_R7B-0/erts/sae' > cd erts && ERL_TOP=/home/luc/tmp/otp_src_R7B-0 make NO_START_SCRIPTS=true opt > /bin/sh: cd: erts: No such file or directory > make[1]: *** [emulator] Error 1 > make[1]: Leaving directory `/home/luc/tmp/otp_src_R7B-0/erts/sae' Strange. I just tried this on my FreeBSD 3.5 machine and it runs ok. In a OTP source directory where I have built before the normal way I did % cd /home/kent/otp_src_R7B-0 % setenv PATH `pwd`/bin:$PATH % setenv ERL_TOP `pwd` % cd erts % gmake sae gmake[1]: Entering directory `/usr/home/kent/otp_src_R7B-0/erts/sae' gmake[2]: Entering directory `/usr/home/kent/otp_src_R7B-0/erts/sae/src' erlc -W -bbeam -I../include -I../../kernel/include -o../ebin ring0.erl erlc -W -bbeam -I../include -I../../kernel/include -o../ebin elink.erl erlc -W -bbeam -I../include -I../../kernel/include -o../ebin primes.erl erlc -W -bbeam -I../include -I../../kernel/include -o../ebin ebundle.erl erl -noshell -pa ../ebin -s ebundle unix /usr/home/kent/otp_src_R7B-0/erts/sae/bin/primes /usr/home/kent/otp_src_R7B-0/erts/sae/ebin/primes.beam gmake[2]: Leaving directory `/usr/home/kent/otp_src_R7B-0/erts/sae/src' gmake[1]: Leaving directory `/usr/home/kent/otp_src_R7B-0/erts/sae' gmake[1]: Entering directory `/usr/home/kent/otp_src_R7B-0/erts/emulator' /usr/bin/perl5 utils/make_preload -old /usr/home/kent/otp_src_R7B-0/erts/sae/ebin/ring0.beam > i386-unknown-freebsd3.5/preload_sae.c . . Have you done "configure" and is the "make" program GNU make? But the build of the sae tools will fail because of the static set of modules listed in "make_sae_tools.erl". % mkdir try_sae; cd try_sae % cp ../../bin/i386-unknown-freebsd3.5/beam_evm . % ls -l -rwxr-xr-x 1 kent wheel 1697419 Oct 14 16:30 beam_evm % strip --remove-section=.note --remove-section=.comment beam_evm -rwxr-xr-x 1 kent wheel 515180 Oct 14 16:30 beam_evm % tar -xzf sae.tgz; cd sae; gmake . . {'init terminating in do_boot',{{case_clause,{error,nofile}}, [{make_sae_tools,get_standard_module,1}.... Among other things, the 'v2_*' modules that are gone. Tried to make a new list with tcsh% truss -f -t open erlc test.erl |& fgrep .beam | sed 's&^.*/&&' | \ sed 's/\.beam.*$//' | sort -u > FILES_COMPILER and the hints given in the file "make_sae_tools.erl", so now the function that lists the modules looks like compiler_modules() -> [%% application, %% application_controller, %% application_master, elink, file_prim, beam_asm, beam_block, beam_bs, % new from running 'truss' in R7 beam_dict, beam_flatten, beam_jump, beam_opcodes, beam_type, c, code, %% code_aux, %% code_server, %% code_server_int, compile, core_lib, % new from running 'truss' in R7 dict, make_sae_tools, %% edlin, epp, erl_bits, % new from running 'truss' in R7 erl_compile, % new from running 'truss' in R7 %% erl_distribution, %% erl_eval, erl_internal, erl_lint, erl_open_port, %% keep erl_parse, erl_posix_msg, %% erl_prim_loader, SPECIAL erl_scan, erlang, %% error_handler, SPECIAL error_logger, error_logger_tty_h, %% ets, file, filename, %% fixtable_server, gen, %% gen_event, gen_server, %% keep %% gen_udp, %% global, %% global_group, group, %% heart, %% inet, %% inet_config, %% inet_db, %% inet_hosts, %% inet_parse, %% inet_udp, %% init, io, io_lib, io_lib_format, io_lib_pretty, %% kernel, %% kernel_config, lists, orddict, % new from running 'truss' in R7 ordsets, os, otp_internal, ram_file, proc_lib, %% keep %% rpc, sets, % new from running 'truss' in R7 %% shell, %% shell_default, string, %% supervisor, %% supervisor_bridge, sys, sys_core_fold, % new from running 'truss' in R7 sys_core_inline, % new from running 'truss' in R7 sys_pre_expand, user, user_drv, user_sup, v3_codegen, % new, v2 is gone in R7 v3_core, % new, v2 is gone in R7 v3_core_opt, % new, v2 is gone in R7 v3_kernel, % new, v2 is gone in R7 v3_life]. % new, v2 is gone in R7 There may be modules missing above or one to much. I only tested to compile a small "Hello world" example using the stand alone compiler. The sae/Makefile assumes '.' is in the PATH, after correcting this (and the 'ehtml' dependency) I was able to build the tools and try to compile a small program. We will try to add the SAE functionality to the normal distribution but I can't say when. The make file needs cleaning up and it needs test cases for use in the daily build process, else it will always get out of sync with the compiler and runtime system. Like it did now. kent From ltaesch@REDACTED Fri Oct 13 19:08:03 2000 From: ltaesch@REDACTED (Luc Taesch) Date: Fri, 13 Oct 2000 19:08:03 +0200 Subject: SAE References: <39E6D307.3677000D@europemail.com> Message-ID: <39E74173.6F2A1BF1@europemail.com> Kent Boortz wrote: follow that ,and it went ok : thks. unfortunately, i fails when running : cd examples/ make ../ecc hello.erl "hello.erl" {ok,hello} ../elink -o hello -m hello -s hello main [luc@REDACTED examples]$ ls Makefile hello hello.beam hello.erl [luc@REDACTED examples]$ ./hello {hello,start,args,'=',["beam_evm"]} Error in process <0.0.0> with exit value: {undef,[{ring0,env,0}]} [luc@REDACTED examples]$ [luc@REDACTED examples]$ > Have you done "configure" and is the "make" program GNU make? > > But the build of the sae tools will fail because of the static set of > modules listed in "make_sae_tools.erl". > > % mkdir try_sae; cd try_sae > % cp ../../bin/i386-unknown-freebsd3.5/beam_evm . > % ls -l > -rwxr-xr-x 1 kent wheel 1697419 Oct 14 16:30 beam_evm > % strip --remove-section=.note --remove-section=.comment beam_evm > -rwxr-xr-x 1 kent wheel 515180 Oct 14 16:30 beam_evm > % tar -xzf sae.tgz; cd sae; gmake here im not sure of what i did the sae .tgz is joe's stuff from the web, hm ? all this is a bit of a wizardry for me, so i didnt wen t any further. but this stuff seems quite cool ! (why did joe mentions it wont compile stuff from std lib ?) -- First, they ignore you. Then, they laugh at you. Then, they fight you. Then, you win. --- Gandhi. Working code is what matter, not your market capitalization. --Kurt granroth -------------- next part -------------- An HTML attachment was scrubbed... URL: From kent@REDACTED Sat Oct 14 19:36:07 2000 From: kent@REDACTED (Kent Boortz) Date: 14 Oct 2000 19:36:07 +0200 Subject: what OS support is needed? In-Reply-To: "Vlad Dumitrescu"'s message of "Sat, 14 Oct 2000 16:29:55 +0200" References: Message-ID: Just as a side note, I have installed QNX on one of the machines in the lab and with the help from you creating and port and if we can get "rsh" and other parts needed by the daily build work, then we will try to make it part of the daily build process. But unfamiliar with QNX I have run into several problems with QNX working with my ethernet card, video card etc so there are lots of work left before I can do any real testing. Is there a QNX expert out there that can offer some help? kent From vladdu@REDACTED Sat Oct 14 19:29:42 2000 From: vladdu@REDACTED (Vlad Dumitrescu) Date: Sat, 14 Oct 2000 19:29:42 +0200 Subject: Windows build Message-ID: Hello! Building erlang on Windows out of the box is not possible, since there are a lot of references to the internal Ericsson build environment (clearcase and such) - unless I have been looking at the wrong makefiles... I'd like to get a try at it - would it be possible to get some "clean" makefiles, or am I on my own? Vlad -------------- next part -------------- An HTML attachment was scrubbed... URL: From kent@REDACTED Sun Oct 15 02:04:16 2000 From: kent@REDACTED (Kent Boortz) Date: 15 Oct 2000 02:04:16 +0200 Subject: Windows build In-Reply-To: "Vlad Dumitrescu"'s message of "Sat, 14 Oct 2000 19:29:42 +0200" References: Message-ID: > Building erlang on Windows out of the box is not possible, since > there are a lot of references to the internal Ericsson build > environment (clearcase and such) - unless I have been looking at the > wrong makefiles... I'd like to get a try at it - would it be > possible to get some "clean" makefiles, or am I on my own? >From us at Ericsson? We haven't had a need to create "clean" make files for the obvious reason, it works for us. So what's in the Open Source release is all we got. The build process is driven from Unix so for us a native Windows build process doesn't gain us anything. I think it also would complicate the daily build process or at least require large changes. I assume you mean "erts/Makefile.win32_cross" when you talk about references to the "internal Ericsson build environment". I find it confusing too, and I'm using the "internal Ericsson build environment" ;-) Did you find the documents in "erts/internal_doc/"? Maybe they can give some hints about how it is built in our environment and how to make it more generic. We are working on simplifications of the Windows build process but it will probably still be closely tied to our internal build environment and of no use to you, sorry, Sorry if I sound negative, we try hard to support the Open Source community and we appreciate the feedback and contributions from it but we are too few here with to much work to do, kent From ltaesch@REDACTED Sat Oct 14 23:07:45 2000 From: ltaesch@REDACTED (Luc Taesch) Date: Sat, 14 Oct 2000 23:07:45 +0200 Subject: bindings to KDE Message-ID: <39E8CB21.518CD34@europemail.com> for informations, following discussions relating, gui, Qt, KDE: from http://dot.kde.org/, Matthias Hoelzer-Kluepfel recently announced Java bindings for DCOP. In other words, Java can now communicate with and control KDE2 applications. A welcome addition to the existing C bindings for DCOP, originally used by the long gone kmapnotify. Then, of course, there's kxmlrpc which provides a bridge to DCOP for anything that can speak HTTP and XML Then, of course, there's kxmlrpc which provides a bridge to DCOP for anything that can speak HTTP and XML -- First, they ignore you. Then, they laugh at you. Then, they fight you. Then, you win. --- Gandhi. Working code is what matter, not your market capitalization. --Kurt granroth -------------- next part -------------- An HTML attachment was scrubbed... URL: From etxuwig@REDACTED Mon Oct 16 09:12:26 2000 From: etxuwig@REDACTED (Ulf Wiger) Date: Mon, 16 Oct 2000 09:12:26 +0200 (MET DST) Subject: what OS support is needed? In-Reply-To: Message-ID: On Sat, 14 Oct 2000, Vlad Dumitrescu wrote: >While waiting for the port (thanks, Sean - how can I help?), I am >wondering what are the minimal OS capabilities needed for running a >minimal Erlang? > >A memory manager is needed, a file system for the code module and a >TCP/IP stack an associated stuff for communicating. But further than >that? Actually, you don't need a file system, as long as you can talk to an erlang node on a machine that does have a file system; then you can load code over the network. Mnesia can also operate in diskless mode. See erl -man erl, specifically the flags '-loader' and '-hosts'. /Uffe -- Ulf Wiger tfn: +46 8 719 81 95 Network Architecture & Product Strategies mob: +46 70 519 81 95 Ericsson Telecom AB, Datacom Networks and IP Services Varuv?gen 9, ?lvsj?, S-126 25 Stockholm, Sweden From per@REDACTED Mon Oct 16 10:16:37 2000 From: per@REDACTED (Per Bergqvist) Date: Mon, 16 Oct 2000 10:16:37 +0200 Subject: Windows build References: Message-ID: <39EAB965.27FC33EC@cellpt.com> There are some other more important problems as well such as the use of MSVCRT setargv and the way quoting of parameters is done which inhibits use of the make files as is. I have a build environment more or less ready for Windows but need somebody to volunteer to assist. I hoped to finish it a couple of weeks ago but so far I haven't turned on my PC at home the last three weeks.... If I knew that somebody really would like to test it I'll probably kick myself somewhere and fix it. /Per Vlad Dumitrescu wrote: > Hello! Building erlang on Windows out of the box is not possible, > since there are a lot of references to the internal Ericsson build > environment (clearcase and such) - unless I have been looking at the > wrong makefiles... I'd like to get a try at it - would it be possible > to get some "clean" makefiles, or am I on my own? Vlad -------------- next part -------------- An HTML attachment was scrubbed... URL: From luke@REDACTED Mon Oct 16 10:32:12 2000 From: luke@REDACTED (Luke Gorrie) Date: 16 Oct 2000 10:32:12 +0200 Subject: what OS support is needed? In-Reply-To: Ulf Wiger's message of "Mon, 16 Oct 2000 09:12:26 +0200 (MET DST)" References: Message-ID: Ulf Wiger writes: > On Sat, 14 Oct 2000, Vlad Dumitrescu wrote: > > >While waiting for the port (thanks, Sean - how can I help?), I am > >wondering what are the minimal OS capabilities needed for running a > >minimal Erlang? > > > >A memory manager is needed, a file system for the code module and a > >TCP/IP stack an associated stuff for communicating. But further than > >that? > > Actually, you don't need a file system, as long as you can talk to an > erlang node on a machine that does have a file system; then you can > load code over the network. Mnesia can also operate in diskless mode. > See erl -man erl, specifically the flags '-loader' and '-hosts'. And you don't need a tcp/ip stack either, just an ethernet driver and Tobbe's erlang implementation of tcp/ip: http://www.bluetail.com/~tobbe/etcp/ Cheers, Luke From Sean.Hinde@REDACTED Mon Oct 16 12:21:35 2000 From: Sean.Hinde@REDACTED (Sean Hinde) Date: Mon, 16 Oct 2000 11:21:35 +0100 Subject: what OS support is needed? Message-ID: <402DD461F109D411977E0008C791C3125651EC@imp02mbx.one2one.co.uk> Luke, The download link seems to be broken.. > And you don't need a tcp/ip stack either, just an ethernet driver and > Tobbe's erlang implementation of tcp/ip: > http://www.bluetail.com/~tobbe/etcp/ Sean NOTICE AND DISCLAIMER: This email (including attachments) is confidential. If you have received this email in error please notify the sender immediately and delete this email from your system without copying or disseminating it or placing any reliance upon its contents. We cannot accept liability for any breaches of confidence arising through use of email. Any opinions expressed in this email (including attachments) are those of the author and do not necessarily reflect our opinions. We will not accept responsibility for any commitments made by our employees outside the scope of our business. We do not warrant the accuracy or completeness of such information. From etxuwig@REDACTED Mon Oct 16 12:28:13 2000 From: etxuwig@REDACTED (Ulf Wiger) Date: Mon, 16 Oct 2000 12:28:13 +0200 (MET DST) Subject: Algorithmic lists Message-ID: Here's a thought: We (a few lunatics at AXD 301) would like to see the lists module support an alternative formulation of lists. A list could be expressed in the following manner: [Head | fun(Head, F)] Example: -module(alglists). -author('etxuwig@REDACTED'). -export([seq/2, foreach/2]). %%-export([Function/Arity, ...]). seq(Start, Stop) when Start < Stop -> [Start | fun(N, F) when N == Stop -> []; (N, F) -> [N+1 | F] end]. foreach(F, []) -> []; foreach(F, [H|T]) when list(T) -> F(H), foreach(F, T); foreach(F, [H|T]) when function(T) -> F(H), foreach(F, T(H, T)). Eshell V4.8.2.7 (abort with ^G) 1> lists:foreach(fun(X) -> io:format("-- ~p~n", [X]) end, lists:seq(1,5)). -- 1 -- 2 -- 3 -- 4 -- 5 ok 2> c(alglists). {ok,alglists} 3> alglists:foreach(fun(X) -> io:format("-- ~p~n", [X]) end, alglists:seq(1,5)). -- 1 -- 2 -- 3 -- 4 -- 5 ok (Perhaps to avoid breaking half the Erlang programs in the universe, functions in lists should not return lists on this form, but should be able to process them.) Let me briefly tell you why we'd like to do this: On several occasions, we've rewritten code that generates a large list on the heap and then traverses it. It's a shame from one perspective, because the new code (which usually uses ets:next/2) is not FP-style, and less elegant -- but it is more well behaved from a memory management perspective, which is its one (but vital) merit. We'd like to keep the concept of list processing, but avoid the large lists, which cause havoc in the GC. What'ya think? Awful? Elegant? BTW, Joe already posted something like this on the erlang-questions list in March '99, but he was using zero-argument funs. http://www.erlang.org/ml-archive/erlang-questions/199903/msg00013.html /Uffe -- Ulf Wiger tfn: +46 8 719 81 95 Strategic Product & System Management mob: +46 70 519 81 95 Ericsson Telecom AB, Datacom Networks and IP Services Varuv?gen 9, ?lvsj?, S-126 25 Stockholm, Sweden From richardc@REDACTED Mon Oct 16 12:56:00 2000 From: richardc@REDACTED (Richard Carlsson) Date: Mon, 16 Oct 2000 12:56:00 +0200 (MET DST) Subject: Algorithmic lists In-Reply-To: Message-ID: On Mon, 16 Oct 2000, Ulf Wiger wrote: > We (a few lunatics at AXD 301) would like to see the lists module > support an alternative formulation of lists. > > A list could be expressed in the following manner: > > [Head | fun(Head, F)] > > [...] > > What'ya think? Awful? Elegant? > > > BTW, Joe already posted something like this on the erlang-questions > list in March '99, but he was using zero-argument funs. > > http://www.erlang.org/ml-archive/erlang-questions/199903/msg00013.html They are indeed elegant, and Joe was correct - they're called lazy lists, and should be implemented using zero-argument funs. To give a type declaration of sorts: LazyList :: [] | [term() | fun() -> LazyList] Writing a module 'lazy_lists' should be a doddle. In fact, I might do it myself. /Richard Carlsson Richard Carlsson (richardc@REDACTED) (This space intentionally left blank.) E-mail: Richard.Carlsson@REDACTED WWW: http://www.csd.uu.se/~richardc/ From tobbe@REDACTED Mon Oct 16 12:58:44 2000 From: tobbe@REDACTED (Torbjorn Tornkvist) Date: 16 Oct 2000 12:58:44 +0200 Subject: what OS support is needed? In-Reply-To: Sean Hinde's message of "Mon, 16 Oct 2000 11:21:35 +0100" References: <402DD461F109D411977E0008C791C3125651EC@imp02mbx.one2one.co.uk> Message-ID: > The download link seems to be broken.. Eh...well, I hadn't intended to make it public quite yet ;-) I still got a bug, so that it isn't possible to setup a listen socket. Anyway, I have now added a tar ball to the download link. Cheers /Tobbe From kent@REDACTED Mon Oct 16 12:59:55 2000 From: kent@REDACTED (Kent Boortz) Date: 16 Oct 2000 12:59:55 +0200 Subject: what OS support is needed? In-Reply-To: Sean Hinde's message of "Mon, 16 Oct 2000 11:21:35 +0100" References: <402DD461F109D411977E0008C791C3125651EC@imp02mbx.one2one.co.uk> Message-ID: > The download link seems to be broken.. http://www.bluetail.com/~tobbe/etcp/etcp-1.0.tgz kent From tobbe@REDACTED Mon Oct 16 13:04:59 2000 From: tobbe@REDACTED (Torbjorn Tornkvist) Date: 16 Oct 2000 13:04:59 +0200 Subject: Algorithmic lists In-Reply-To: Richard Carlsson's message of "Mon, 16 Oct 2000 12:56:00 +0200 (MET DST)" References: Message-ID: Talking of lazy lists, Phil Waldler once helped me writing the following stuff. Cheers /Tobbe -module(ns). -author('tnt@REDACTED'). -compile(export_all). -define( return(X) , fun() -> X end ). -define( force(X) , begin XXXfunnyvar=X, XXXfunnyvar() end ). -define( vforce(V,X) , begin V=X, V() end ). -define( bind(X,D,K) , fun() -> X=?vforce(XXXfunnyvar1,D), ?vforce(XXXfunnyvar2,K) end ). -define( cons(H,T) , ?return({cons,H,T}) ). -define( nil(), ?return(nil) ). %% A lazy first... first(0,_) -> ?nil(); first(N,S) when N>0 -> ?bind(S1,S, case S1 of nil -> ?nil(); {cons,H,T} -> ?cons(H,first(N-1,T)) end). %% 24> statistics(reductions),ns:first(7,ns:primes()),statistics(reductions). %% {1705891,61} %% 25> statistics(reductions),ns:stream_to_list(ns:first(7,ns:primes())),statistics(reductions). %% {1708314,616} %% 6> statistics(reductions),streams:first(7,streams:primes()),statistics(reductions). %% {1712785,576} nth(0,S) -> case ?force(S) of {cons,H,T} -> H end; nth(N,S) when N>0 -> case ?force(S) of {cons,_,T} -> nth(N-1,T) end. map(F,S) -> ?bind(S1,S, case S1 of nil -> ?nil(); {cons,H,T} -> ?cons(F(H),map(F,T)) end). scale(C,S) -> map(fun(X) -> X*C end,S). double(S) -> scale(2,S). %% Example: %% %% 15> ns:stream_to_list(ns:first(8,ns:append(ns:first(5,ns:naturals()),ns:first(5,ns:naturals())))). %% [0,1,2,3,4,0,1,2] %% 77> statistics(reductions),ns:stream_to_list(ns:first(8,ns:append(ns:naturals(),ns:naturals()))),statistics(reductions). %% {1909897,270} %% 78> statistics(reductions),ns:lf(8,lists:append(lists:seq(1,500),lists:seq(1,500))),statistics(reductions). %% {1919732,1157} append(S,R) -> ?bind(S1,S, case S1 of nil -> R; {cons,H,T} -> ?cons(H,append(T,R)) end). naturals() -> integers_starting_from(0). integers_starting_from(N) -> ?cons(N,integers_starting_from(N+1)). primes() -> sieve(integers_starting_from(2)). sieve(S) -> ?bind(S1,S, (case S1 of nil -> ?nil(); {cons,H,T} -> Pred = fun(X) -> 'not'(divisible(X,H)) end, ?cons(H,sieve(filter(Pred,T))) end)). filter(Pred,S) -> ?bind(S1,S, case S1 of nil -> ?nil(); {cons,H,T} -> case Pred(H) of true -> ?cons(H,filter(Pred,T)); false -> filter(Pred,T) end end). divisible(X,Y) -> if X rem Y == 0 -> true; true -> false end. 'not'(true) -> false; 'not'(false) -> true. gcd(A,0) -> A; gcd(A,B) -> gcd(B,A rem B). %% Try this: %% 22> ns:stream_to_list(ns:first(10,ns:map(fun(X)-> round(X) end,ns:scale(100,ns:random_numbers(random:seed()))))). %% [9,44,72,95,50,31,60,92,67,48] random_numbers(Seed) -> ?cons(random:uniform(),random_numbers(Seed)). cesaro_stream() -> F = fun(R1,R2) -> case gcd(R1,R2) of 1 -> true; _ -> false end end, map_successive_pairs(F,map(fun(X)-> round(X) end,scale(100,random_numbers(random:seed())))). map_successive_pairs(F,S) -> ?bind(S1,S, case S1 of {cons,H,T} -> case ?force(T) of {cons,H2,T2} -> ?cons(F(H,H2), map_successive_pairs(F,T2)) end end). monte_carlo(S,Nt,Nf) -> ?bind(S1,S, (case S1 of {cons,H,T} -> Next = fun(Nnt,Nnf) -> Q = Nnt+Nnf, ?cons(Nnt/Q,monte_carlo(T,Nnt,Nnf)) end, case H of true -> Next(Nt+1,Nf); false -> Next(Nt,Nf+1) end end)). %% An approximation of pi. %% We will use the fact that the probability that %% two integers choosen at random will have no factors %% in common (i.e their GCD is 1) is 6/pi**2. The %% fraction of times the test is passed gives us an %% estimate of this probability. So the further you %% look into the stream, the better approximation you'll get. pi() -> map(fun(P) -> math:sqrt(6/P) end, monte_carlo(cesaro_stream(),0,0)). %% Converting routines: streams <--> lists stream_to_list(S) -> case ?force(S) of {cons,H,T} -> [H|stream_to_list(T)]; nil -> [] end. list_to_stream([H|T]) -> ?cons(H,list_to_stream(T)); list_to_stream([]) -> ?nil(). %% Misc. routines rv(File,NewFile) when list(File) -> M = erl_kernel:file(File), {ok,T,_} = erl_kernel:module(M), {ok,F} = file:open(NewFile,write), erl_kernel:pp(T,F), file:close(F). lf(0,_) -> []; lf(N,[H|T]) -> [H|lf(N-1,T)]. From thomas@REDACTED Mon Oct 16 13:08:54 2000 From: thomas@REDACTED (Thomas Arts) Date: Mon, 16 Oct 2000 13:08:54 +0200 Subject: Algorithmic lists References: Message-ID: <39EAE1C6.26547F5E@cslab.ericsson.se> Ulf Wiger wrote: > > Here's a thought: > > We (a few lunatics at AXD 301) would like to see the lists module > support an alternative formulation of lists. > > A list could be expressed in the following manner: > > [Head | fun(Head, F)] I don't really like that notation, since the tail of a list is no longer a list now (but a function returning a list). I find it more natural to return a tuple, a kind of continuation. Basically you try to introduce lazy evaluation in an eager language. I think this is indeed sometimes needed (I wrote a lazy version of "all subsets of a set" recently). When dealing with the create and filter paradigm, one basically wants to generate a list and drop all items that do not respect a certain filter function. That's the same here, except that instead of just removing it from the list, you add a side-effect. It would be nice if this could be established by a smart, lazy list-comprehension: [ X || X<-seq(1,5), condition(X) ] where condition(X) is condition(X) -> io:format("-- ~p~n", [X]), false. The result would be the empty list, but (as far as I recall the Erlang semantics) the list seq(1,5) is generated before the tests on the arguments is performed. It would be nice to have a lazy list-comprehension as well. An element is computed, the filter is applied and so on. Syntax could be basically the same, as long as no side-conditions are allowed in the generators, I think the execution of the strict and the eager variant should be the same. Solving it this way, would keep your code more readable ;0). Implementation in the compiler seems not so hard to me, but let a compiler writer comment on that. With respect to your solution, why not using the tuple construct: implicitely defining the continuation type: +deftype continuation(T) = {} | {T, fun(T,continuation(T)) -> continuation(T)}. +type seq(int(),int()) -> continuation(int()). seq(Start, Stop) when Start < Stop -> {Start , fun(N, F) when N == Stop -> {}; (N, F) -> {N+1 , F} end]. +type foreach(fun(A) -> B, continuation(A)) -> void(). foreach(F, {}) -> void; foreach(F, {H,C}) -> F(H), foreach(F, C(H, C)). Sure, overloading of foreach needs the two lines > foreach(F, []) -> > []; > foreach(F, [H|T]) -> > F(H), > foreach(F, T); as well, because of Erlang's structure. Implementing it within Erlang in this way has the disadvantage that you need to program quite a lot, which is not the case if it would have been given as a language primitive. /Thomas > > > Example: > > -module(alglists). > -author('etxuwig@REDACTED'). > > -export([seq/2, foreach/2]). > > %%-export([Function/Arity, ...]). > > seq(Start, Stop) when Start < Stop -> > [Start | fun(N, F) when N == Stop -> > []; > (N, F) -> > [N+1 | F] > end]. > > foreach(F, []) -> > []; > foreach(F, [H|T]) when list(T) -> > F(H), > foreach(F, T); > foreach(F, [H|T]) when function(T) -> > F(H), > foreach(F, T(H, T)). > > Eshell V4.8.2.7 (abort with ^G) > > 1> lists:foreach(fun(X) -> io:format("-- ~p~n", [X]) end, > lists:seq(1,5)). -- 1 > -- 2 > -- 3 > -- 4 > -- 5 > ok > 2> c(alglists). > {ok,alglists} > 3> alglists:foreach(fun(X) -> io:format("-- ~p~n", [X]) end, > alglists:seq(1,5)). > -- 1 > -- 2 > -- 3 > -- 4 > -- 5 > ok > > (Perhaps to avoid breaking half the Erlang programs in the universe, > functions in lists should not return lists on this form, but should be > able to process them.) > > Let me briefly tell you why we'd like to do this: > > On several occasions, we've rewritten code that generates a large > list on the heap and then traverses it. It's a shame from one > perspective, because the new code (which usually uses ets:next/2) is > not FP-style, and less elegant -- but it is more well behaved from a > memory management perspective, which is its one (but vital) merit. > We'd like to keep the concept of list processing, but avoid the large > lists, which cause havoc in the GC. > > What'ya think? Awful? Elegant? > > BTW, Joe already posted something like this on the erlang-questions > list in March '99, but he was using zero-argument funs. > > http://www.erlang.org/ml-archive/erlang-questions/199903/msg00013.html > > /Uffe > -- > Ulf Wiger tfn: +46 8 719 81 95 > Strategic Product & System Management mob: +46 70 519 81 95 > Ericsson Telecom AB, Datacom Networks and IP Services > Varuv?gen 9, ?lvsj?, S-126 25 Stockholm, Sweden From simonb@REDACTED Mon Oct 16 13:18:08 2000 From: simonb@REDACTED (Simon Bennett) Date: Mon, 16 Oct 2000 12:18:08 +0100 Subject: Algorithmic lists References: Message-ID: <39EAE3F0.47A3397A@terminus.ericsson.se> Is this what is known as lazy evaluation in functional programming terms? i.e. it allows you to define a possibly infinite list, and the values in the list are only evaluated and returned as and when they are required? If this is the case, it brings Erlang closer to the idea of an executable specification (which people were discussing in the context of "Who is a Programmer?") as you can define a set by intension rather than by extension. Simon Bennett ___________________________________________________________________ Simon Bennett E-mail: simonb@REDACTED Ericsson Intracom http://www.ericsson.co.uk/UK/intracom 1 Bede Island Road Voice (UK) 0116 2542400 Leicester Voice (int) +44 116 2542400 England Voice ECN: 832 707 ext 232 LE2 7EU Fax: +44 (0)116 2046111 ___________________________________________________________________ Ulf Wiger wrote: > > Here's a thought: > > We (a few lunatics at AXD 301) would like to see the lists module > support an alternative formulation of lists. > > A list could be expressed in the following manner: > > [Head | fun(Head, F)] > > Example: > > -module(alglists). > -author('etxuwig@REDACTED'). > > -export([seq/2, foreach/2]). > > %%-export([Function/Arity, ...]). > > seq(Start, Stop) when Start < Stop -> > [Start | fun(N, F) when N == Stop -> > []; > (N, F) -> > [N+1 | F] > end]. > > foreach(F, []) -> > []; > foreach(F, [H|T]) when list(T) -> > F(H), > foreach(F, T); > foreach(F, [H|T]) when function(T) -> > F(H), > foreach(F, T(H, T)). > > Eshell V4.8.2.7 (abort with ^G) > > 1> lists:foreach(fun(X) -> io:format("-- ~p~n", [X]) end, > lists:seq(1,5)). -- 1 > -- 2 > -- 3 > -- 4 > -- 5 > ok > 2> c(alglists). > {ok,alglists} > 3> alglists:foreach(fun(X) -> io:format("-- ~p~n", [X]) end, > alglists:seq(1,5)). > -- 1 > -- 2 > -- 3 > -- 4 > -- 5 > ok > > (Perhaps to avoid breaking half the Erlang programs in the universe, > functions in lists should not return lists on this form, but should be > able to process them.) > > Let me briefly tell you why we'd like to do this: > > On several occasions, we've rewritten code that generates a large > list on the heap and then traverses it. It's a shame from one > perspective, because the new code (which usually uses ets:next/2) is > not FP-style, and less elegant -- but it is more well behaved from a > memory management perspective, which is its one (but vital) merit. > We'd like to keep the concept of list processing, but avoid the large > lists, which cause havoc in the GC. > > What'ya think? Awful? Elegant? > > BTW, Joe already posted something like this on the erlang-questions > list in March '99, but he was using zero-argument funs. > > http://www.erlang.org/ml-archive/erlang-questions/199903/msg00013.html > > /Uffe > -- > Ulf Wiger tfn: +46 8 719 81 95 > Strategic Product & System Management mob: +46 70 519 81 95 > Ericsson Telecom AB, Datacom Networks and IP Services > Varuv?gen 9, ?lvsj?, S-126 25 Stockholm, Sweden From luc.taesch@REDACTED Mon Oct 16 13:27:59 2000 From: luc.taesch@REDACTED (luc.taesch@REDACTED) Date: Mon, 16 Oct 2000 13:27:59 +0200 Subject: lazy, lazy.. Message-ID: i did experiment a bit on lazy lists ( following http://www.erlang.org/doc/r7b/doc/extensions/part_frame.html), (basically, did map and fold, and so on) and i found it interesting . still: - i found them not making things more easy ( but this argument mail fall short , if an official lazy_list module exists, as it then becomes standard) ( and i was contemeplating the fact im newby to FP, which may hinder some hidden complex aspects) . - some powerful things could be done( but tricky). (e.g: when "walking " a tree/graph, the walking function can be depending of the node, like a "strategy pattern") this is powerful, but tricky to read afterwards. - in a similar manner to uffe statement, my lazy walk was walking a non existing tree yet, as it was following a db schema , loading mnesia data as it went. the good point is that is was loading just as necessary ( rather than loading half of the db, to later retain one 10th of it) (same issue when walking xml files). i finally stops my investigations, but i can dig out the code in my cvs, if anybody interested. One thing id thought about was to enable a HOF on tree and graph ( like map and fold for list), but wasnt dependent of the underlying data strucuture ( the fun hides it for u)). a walk/insert/delete... can be paramtrized by -one function to walk the node -one to "do the job " u wanna do on it. On Mon, 16 Oct 2000, Ulf Wiger wrote: > We (a few lunatics at AXD 301) would like to see the lists module > support an alternative formulation of lists. > > A list could be expressed in the following manner: > > [Head | fun(Head, F)] > > [...] > > What'ya think? Awful? Elegant? > > > BTW, Joe already posted something like this on the erlang-questions > list in March '99, but he was using zero-argument funs. > > http://www.erlang.org/ml-archive/erlang-questions/199903/msg00013.html They are indeed elegant, and Joe was correct - they're called lazy lists, and should be implemented using zero-argument funs. To give a type declaration of sorts: LazyList :: [] | [term() | fun() -> LazyList] Writing a module 'lazy_lists' should be a doddle. In fact, I might do it myself. /Richard Carlsson Richard Carlsson (richardc@REDACTED) (This space intentionally left blank.) E-mail: Richard.Carlsson@REDACTED WWW: http://www.csd.uu.se/~richardc/ From richardc@REDACTED Mon Oct 16 13:32:49 2000 From: richardc@REDACTED (Richard Carlsson) Date: Mon, 16 Oct 2000 13:32:49 +0200 (MET DST) Subject: Algorithmic lists In-Reply-To: <39EAE1C6.26547F5E@cslab.ericsson.se> Message-ID: On Mon, 16 Oct 2000, Thomas Arts wrote: > Ulf Wiger wrote: > > > > A list could be expressed in the following manner: > > > > [Head | fun(Head, F)] > > I don't really like that notation, since the tail of a list is no > longer a list now (but a function returning a list). I find it more > natural to return a tuple, a kind of continuation. Yes, however, the cons cells are much more efficient in terms of memory allocation (in current Erlang implementations), only needing 2 words where a tuple needs three. I don't find it unreasonable to use cons cells for certain datatype representations, when they map so naturally to "cons or nil" as in this case. "cons cell" does not always have to imply "proper list", although for general purpose programming cons cells should not be used. > It would be nice if this could be established by a smart, > lazy list-comprehension: > > [ X || X<-seq(1,5), condition(X) ] I agree that it would be quite nice for Erlang to have language support for lazy list comprehensions. As I recall, when list comprehensions were first about to be introduced in Erlang, there was some debate about whether they should have lazy semantics or not. However, I think that it was correct that normal list comprehensions in Erlang should have strict semantics. It should probably not be a problem to implement a lazy version also. /Richard Carlsson Richard Carlsson (richardc@REDACTED) (This space intentionally left blank.) E-mail: Richard.Carlsson@REDACTED WWW: http://www.csd.uu.se/~richardc/ From thomas@REDACTED Mon Oct 16 13:44:45 2000 From: thomas@REDACTED (Thomas Arts) Date: Mon, 16 Oct 2000 13:44:45 +0200 Subject: Algorithmic lists References: Message-ID: <39EAEA2D.187C1286@cslab.ericsson.se> Richard Carlsson wrote: > > On Mon, 16 Oct 2000, Thomas Arts wrote: > > > Ulf Wiger wrote: > > > > > > A list could be expressed in the following manner: > > > > > > [Head | fun(Head, F)] > > > > I don't really like that notation, since the tail of a list is no > > longer a list now (but a function returning a list). I find it more > > natural to return a tuple, a kind of continuation. > > Yes, however, the cons cells are much more efficient in terms of memory > allocation (in current Erlang implementations), only needing 2 words where > a tuple needs three. I don't find it unreasonable to use cons cells for > certain datatype representations, when they map so naturally to "cons or > nil" as in this case. "cons cell" does not always have to imply "proper > list", although for general purpose programming cons cells should not be > used. I am horrified by the idea to find the AXD 301 software full with cons cells, just in order to save those few bytes. In particular since in these lazy lists, the elements have a very short lifetime (Ulf wants to generate a side-effect and throw the value away), it would only save you one word. If efficiency turns outto be so much an issue, than adding a new type to Erlang (similar to cons, but with different notiation, would be preferable). /Thomas From bjorn@REDACTED Mon Oct 16 14:03:22 2000 From: bjorn@REDACTED (Bjorn Gustavsson) Date: 16 Oct 2000 14:03:22 +0200 Subject: SAE In-Reply-To: "Vlad Dumitrescu"'s message of "Sat, 14 Oct 2000 00:30:04 +0200" References: Message-ID: We did some more work on SAE for R7. SAE should now work for Windows. On Windows, the emulator is an DLL (even if you don't use SAE). We also removed the need for conditional compilation of the emulator source code. There are still no tools. We might do the tools for R8, or possibly as a patch for R7B. Joe's tools might not work with the new version of SAE in R7B. /Bj?rn "Vlad Dumitrescu" writes: > Hi! > > It seems that there are no news about SAE in the latest release... Has anything more been done with it, or is Joe Armstrong's "how-to" at http://www.bluetail.com/~joe/sae/sae.html the latest development? > > I remember someone (Tony Rogvall?) mentioned something about fiddling with SAE in order to use shared libraries. Any progress? > > What about doing the same under Windows? The method used now won't work, as the "#!/bin/usr/beam_evm" header isn't Windows-ish... :-) > > regards, > /Vlad > -- Bj?rn Gustavsson Ericsson Utvecklings AB bjorn@REDACTED ?T2/UAB/F/P BOX 1505 +46 8 727 56 87 125 25 ?lvsj? From etxuwig@REDACTED Mon Oct 16 14:19:56 2000 From: etxuwig@REDACTED (Ulf Wiger) Date: Mon, 16 Oct 2000 14:19:56 +0200 (MET DST) Subject: Algorithmic lists In-Reply-To: <39EAEA2D.187C1286@cslab.ericsson.se> Message-ID: We (Kurt Jonsson and I) also discussed using tuples. It has the disadvantage for us that it involves more changes to existing code (which is naturally using the list "cons syntax" today.) /Uffe On Mon, 16 Oct 2000, Thomas Arts wrote: >I am horrified by the idea to find the AXD 301 software full with >cons cells, just in order to save those few bytes. In particular since >in these lazy lists, the elements have a very short lifetime (Ulf wants >to generate a side-effect and throw the value away), it would only >save you one word. If efficiency turns outto be so much an issue, than >adding a new type to Erlang (similar to cons, but with different notiation, >would be preferable). -- Ulf Wiger tfn: +46 8 719 81 95 Strategic Product & System Management mob: +46 70 519 81 95 Ericsson Telecom AB, Datacom Networks and IP Services Varuv?gen 9, ?lvsj?, S-126 25 Stockholm, Sweden From bjorn@REDACTED Mon Oct 16 14:26:45 2000 From: bjorn@REDACTED (Bjorn Gustavsson) Date: 16 Oct 2000 14:26:45 +0200 Subject: Plans for stricter record semantics in R8 Message-ID: In R8, we preliminary plan to stricten the test for valid records in cases like: func(R) -> A = R#r.field, ... R#r{field=New}. In the present implementation, there is no test that R is bound to a record of the correct size and that the first element is the record tag. (Retrieving a field is simple a call to element/2 and updating one field is done through a call to setelement/3.) However, if you access more than one field at the same time, as in func(R) -> R#r{field1=A,field2=B} = R, ... R#r{field1=New1,field2=New2}. the type of R will be checked. In R8, we plan to always check the record types. To avoid slowing down record operations, we plan to introduce some new optimisations: (1) Using a destructive version of setelement/3 to speed up updates of multiple fields. (The update of the first field will be done with the normal setelement/3, to avoid both nasty semantic problems and nasty crashes in the garbage collector. :-) ) (2) Avoiding multiple type tests on record variables. Typically, the same record is typically used several times within a single function. In the present implementation, type tests (at least to ensure that it is a tuple) will be done every time the record is accessed. It is enough to test the type once. On the average, we except that these optimsations will actually make record operations faster. Drawbacks: Sloppy code that depends on the records tests not being done will stop to work. /Bj?rn -- Bj?rn Gustavsson Ericsson Utvecklings AB bjorn@REDACTED ?T2/UAB/F/P BOX 1505 +46 8 727 56 87 125 25 ?lvsj? From bjorn@REDACTED Mon Oct 16 14:39:39 2000 From: bjorn@REDACTED (Bjorn Gustavsson) Date: 16 Oct 2000 14:39:39 +0200 Subject: Algorithmic lists In-Reply-To: Thomas Arts's message of "Mon, 16 Oct 2000 13:44:45 +0200" References: <39EAEA2D.187C1286@cslab.ericsson.se> Message-ID: Thomas Arts writes: > I am horrified by the idea to find the AXD 301 software full with > cons cells, just in order to save those few bytes. In particular since > in these lazy lists, the elements have a very short lifetime (Ulf wants > to generate a side-effect and throw the value away), it would only > save you one word. If efficiency turns outto be so much an issue, than > adding a new type to Erlang (similar to cons, but with different notiation, > would be preferable). We don't have any free tags to add a new cons-like type. A new type would need a header word indicating the type and size, and would therefore not be smaller than a tuple. We already use non-normalised lists in Erlang and OTP. The list_to_binary/1 BIF and ports accept binaries in list tails. > > > /Thomas > /Bj?rn -- Bj?rn Gustavsson Ericsson Utvecklings AB bjorn@REDACTED ?T2/UAB/F/P BOX 1505 +46 8 727 56 87 125 25 ?lvsj? From enano@REDACTED Mon Oct 16 17:25:43 2000 From: enano@REDACTED (Miguel Barreiro Paz) Date: Mon, 16 Oct 2000 17:25:43 +0200 (CEST) Subject: multiple heaps Message-ID: Is there any reason why each process keeps its own separate heap instead of one global heap? Doesn't the cost of sending large complex structures as messages among processes in the same node justify it? - ie., instead of just copying pointers to the process mailbox. Regards, Miguel From raimo@REDACTED Mon Oct 16 18:21:52 2000 From: raimo@REDACTED (Raimo Niskanen) Date: Mon, 16 Oct 2000 18:21:52 +0200 Subject: multiple heaps References: Message-ID: <39EB2B20.C09C366D@erix.ericsson.se> The separate heaps improve the soft real time characteristics of the emulator - the garbage collector works on the process heaps. One large heap would lead to longer idle times for garbage collection, unless the large garbage collector would be very incremental and intricate. There has been a few experiments with common heap and garbage collection, but the results were disappointing. It seems that with modern microprocessors the cost to copy data between heaps is cheap, if the memory pages happens to be in the processor cache, and with a large common heap the probability of a chache miss increases and eats up the gain of not having to copy. And frankly, the structures copied between heaps are not that large and complex on the average. For bulk data transfer You should pass binary objects instead, which are allocated in common storage off heap (if not smaller than 64 bytes (for the current BEAM emulator)). / Raimo Niskanen, Erlang/OTP Miguel Barreiro Paz wrote: > > Is there any reason why each process keeps its own separate heap > instead of one global heap? Doesn't the cost of sending large complex > structures as messages among processes in the same node justify it? - ie., > instead of just copying pointers to the process mailbox. > > Regards, > > Miguel From Erik.Johansson@REDACTED Mon Oct 16 18:26:27 2000 From: Erik.Johansson@REDACTED (Erik.Johansson) Date: Mon, 16 Oct 2000 18:26:27 +0200 Subject: multiple heaps References: Message-ID: <031001c0378d$d575fdd0$980cee82@it.uu.se> > Is there any reason why each process keeps its own separate heap instead of one global heap? Yes: The time for garbage-collection can be kept down this way. If all processes use one global heap then the garbage collector would have to scan all processes stacks in order to get the root-set. Also, some programmers use the fact that each process has its own heap as a poor-mans memory manager, by spawning a separate process for a computation that will do a short computation generating a lot of garbage the whole heap of the new process can be reclaimed at once when the computation is finished - no GC needed. In real-time applications one do not want one memory hungry process to be able to generate garbage that other, perhaps more important, processes has to GC. > Doesn't the cost of sending large complex > structures as messages among processes in the same node justify it? - ie., > instead of just copying pointers to the process mailbox. Perhaps. The question is whether large messages are sent often enough. For the programs we (The HiPE group) have access to, which admittedly are few, measurements show that most messages are small. We plan to implement a shared heap with several generations to test whether this is the case or not. /Erik Johansson http://www.csd.uu.se/projects/hipe/ From cesarini@REDACTED Mon Oct 16 18:32:31 2000 From: cesarini@REDACTED (Francesco Cesarini) Date: Mon, 16 Oct 2000 17:32:31 +0100 Subject: Plans for stricter record semantics in R8 References: Message-ID: <39EB2D9F.AB86AD74@terminus.ericsson.se> Bjorn Gustavsson wrote: > > In R8, we preliminary plan to stricten the test for valid records Great. Are there any plans of inserting generic functions on records as well (or implementing O'Keef's suggestion from 1998)? Due to their implementation, it is today impossible to write generic code using different record types. There are however many cases where different records can be treated equally as they share some common denominators. Generic code of this kind is today handled by using the tuple representation of records. (Do as I say, don't do as I do is what I end up preaching....) And the code is static anyhow, as every time you add a new record, you have to recompile the file with the generic stuff (or go through a call back module which makes the code yucky and hard to follow). Functions (BIFs) could include get_record(RecordType) -> #RecordType{} get_record_name(Record) -> RecordType get_record_fields(RecordName) -> [FieldName1,...] lookup_record_field(Record, FieldName1) -> Element Just in time for xmas?? :-) Regards, FC -- Francesco Cesarini Erlang/OTP consultant Cellular: INT+44-7776 250381 ECN: 832-707192 http://welcome.to/cesarini.consulting From enano@REDACTED Mon Oct 16 20:23:22 2000 From: enano@REDACTED (Miguel Barreiro Paz) Date: Mon, 16 Oct 2000 20:23:22 +0200 (CEST) Subject: multiple heaps In-Reply-To: <39EB2B20.C09C366D@erix.ericsson.se> Message-ID: Thanks for the answers. Summarizing, > The separate heaps improve the soft real time characteristics of the > emulator Now, wouldn't a shared heap with more generations achieve the same effect? - quoting Erik Johansson: > We plan to implement a shared heap with several generations to test > whether this is the case or not. so, we may eventually find out. Admittedly, it might be more complex. > It seems that with modern microprocessors the cost to copy data > between heaps is cheap, if the memory pages happens to be in the > processor cache, I suppose copying includes relocating list pointers here, so long lists would have a bigger copy penalty than large monolithic chunks of data - certainly, modern processors are likely to do an addition for free, hidden in the access latency. > For bulk data transfer You should pass binary objects instead, which > are allocated in common storage off heap (if not smaller than 64 bytes Yes, thanks for the suggestion; This is however a bit of a hack - converting a list to a binary and then into a list again is not the cleanest and clearest way one might wish. By the way, what is the current size limit for the common binary storage? (I seem to recall it was 64MB or so in R6A, which became a problem here). Regards, Miguel From ltaesch@REDACTED Sun Oct 15 21:09:54 2000 From: ltaesch@REDACTED (Luc Taesch) Date: Sun, 15 Oct 2000 21:09:54 +0200 Subject: Plans for stricter record semantics in R8 Message-ID: <39EA0102.64E0745@abom.com> > Bjorn Gustavsson wrote: > > > > In R8, we preliminary plan to stricten the test for valid records > > Great. Are there any plans of inserting generic functions on records as > well (or implementing O'Keef's suggestion from 1998)? > i would not mind a link ? > Due to their > implementation, it is today impossible to write generic code using > different record types. There are however many cases where different > records can be treated equally as they share some common denominators. > > Generic code of this kind is today handled by using the tuple > representation of records. (Do as I say, don't do as I do is what I end > up preaching....) And the code is static anyhow, as every time you add a > new record, you have to recompile the file with the generic stuff (or go > through a call back module which makes the code yucky and hard to > follow). > > Functions (BIFs) could include > > get_record(RecordType) -> #RecordType{} > get_record_name(Record) -> RecordType > get_record_fields(RecordName) -> [FieldName1,...] > lookup_record_field(Record, FieldName1) -> Element > here are the corresponding functions in attached files Function : prototype/2 %% Purpose : create One Empty record of type Table, according to Schema %%--------------------------------------- %%--------------------------------------- %% Function : info/2 %% Purpose : entity | relations for a Record %% Arg: entity| relation, Record, grmSchema %%--------------------------------------- grm_mnesia:field_value(Record,Field)-> %%--------------------------------------- grm_mnesia:retrieve(Table,Key) when atom(Key) %%--------------------------------------- Function :grm_mnesia: retrieve/3 %% Purpose : retrieve the records referenced by a given record %% Arg: Relation, Record | [Record], Schema in the attached grm_mnesia, files, ull find the corresponding functions. (still work in progress). they suppose your database is defined in a grm schema like schema()-> [ {entity, [description,indexing,responsabilities,services,suppliers,states,invariant,component,precondition,postcondition,predicate,menu,commands,queries,dialog]}, {relation, [{component,composition,states}, {states,reference,description}, {component,reference,description}, {component,composition,commands}, {component,composition,queries}, % {features,reference,description}, % {features,composition,commands}, % {features,composition,queries}, with manipulation functions in grm_schema supported_relationships()-> [attribute,dependency,reference,composition]. also an example kit_schema, and the kit_lib in case of dependencies. this is work in progress, and need a lot of restructuring ( a few things are in grm_schema/mnesia now, but need some to be moved out.) (GRM stands for generic relationship management, i believe, (kilov work on formal semantics, an iso standard now. (kilov worked in RM_ODP may be known by you, telecom guys. i can search links if interested) i was surprised how little overhead i found (maybe 10-20%, i was expecting 100-200, compared to "compiled coded" records. (well , i dont have soft real time requirement) btw, there is a few utilitaries that create db schema, or records headers based on the schema %% Purpose : give the records definition (.hrl) %% Arg: headers,Schema %% Return : -record (tablename,[fieldsname]), string-ified. %%--------------------------------------- info(headers,S)-> %%--------------------------------------- %% Function : info/2 %% Purpose : give the table definition (like mnesia:save_to_text would) %% Arg: table,Schema %% Return : {table, [{tablename,[fieldsname]}]} %%--------------------------------------- info(tables,S)-> got some more things, if interested. > Just in time for xmas?? :-) > > Regards, > -- First, they ignore you. Then, they laugh at you. Then, they fight you. Then, you win. --- Gandhi. Working code is what matter, not your market capitalization. --Kurt granroth -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- %% ``The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in %% compliance with the License. You should have received a copy of the %% Erlang Public License along with this software. If not, it can be %% retrieved via the world wide web at http://www.erlang.org/. %% %% Software distributed under the License is distributed on an "AS IS" %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See %% the License for the specific language governing rights and limitations %% under the License. %% %% The Initial Developer of the Original Code is luc abom. %% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings %% AB. All Rights Reserved.'' %% %% $Id: grm_schema.erl,v 1.7 2000/10/13 16:59:29 luc Exp $ %% -module(grm_schema). -author('luc@REDACTED'). -compile([export_all]). -export([new/1, add/3, fetch/2,fetch/3, composition/2,dependency/2,reference/2, supported_relationships/0]). %%--------------------------------------- %% Function : new/1 %% Purpose : create a new grm schema %% Arg: schema %% Return : empty schema %%--------------------------------------- new(schema)->schema([],[]) . %%--------------------------------------- %% Function : add/3 %% Purpose : add a new entity to the schema %% Arg: entity, Entity= atom(),Schema %% Return : updated schema %%--------------------------------------- add(entity,E, S)-> false= exist(entity,E,S), %%precondition: uniqueness schema( combine_item( E,fetch(entity,S)), fetch(relation,S)); %%--------------------------------------- %% Function : add/4 %% Purpose : add a new relation to the schema %% Arg: relation, FromEntity= atom(),{relation, ToEntity},Schema %% Return : updated schema %%--------------------------------------- add(relation, Relation, S) -> is_add_able(relation,Relation,S), %% precondition : valid relation schema( fetch(entity,S), combine_item(Relation,fetch(relation,S))). %%--------------------------------------- %% Function : fetch/2 %% Purpose : fetch entities or relationsfrom the schema %% Arg: entity,Schema, or relation, Schema %% Return : list of entities {entity,[atoms()]}, or { relation, {entity,relation,[entities] %%--------------------------------------- fetch(entity,[{entity,E},{relation,R}])-> % {value,{entity,K}}=lists:keysearch(entity,1,S), E ; fetch(relation,[{entity,E},{relation,R}]) -> lists:keysort(2,R). %%--------------------------------------- %% Function : fetch/3 %% Purpose : fetch relation (resp .specific relation) entity from the schema %% Arg: relation | supportedRelations(), FromEntity= atom(),{relation, ToEntity},Schema %% Return : [{{From,Rel,To}}] (=filtered schema) %%--------------------------------------- fetch(relation,From,S) -> R=fetch(relation,S), K=lists:filter(fun({FromX,Rel,To})-> case FromX of From->true; _ ->false end end ,R) %{value,K}=lists:keysearch(From,1,R), , K; fetch(Relation,From,S)-> fetch(relation,From,Relation,S). %% not exported fetch(relation,From,Rel,S) -> R=fetch(relation,S), K=lists:filter(fun({FromX,RelX,To})-> case {FromX,RelX} of {From,Rel}->true; _ ->false end end ,R) %{value,K}=lists:keysearch(From,1,R), , K. %%------------------------------------------ %% internals %%---------------------------------------- %%--------------------------------------- %% Function : schema/2 %% Purpose : build a schema structure %% Arg: EntityList= [atom()],relationList [relations()]} %% Return : [{entity, EntityList},{relation,relationList}] %%--------------------------------------- schema(EntityList,RelationList)-> [{entity,EntityList},{relation,RelationList}]. relation(From,RelationType,To)-> {From,RelationType,To}. composition(From,To)-> relation(From,composition,To). reference(From,To)-> relation(From,reference,To). dependency(From,To)-> relation(From,dependency,To). attribute(From,To)-> relation(From,attribute,To). %%--------------------------------------- %% Function : is_valid/3 %% Purpose : check a relationship or an entity %% Arg: Relationship,Schema %% Return : true if both entity exists in the schema, the relation is supported, and doesnt exists already (unique) %%--------------------------------------- is_add_able(relation,{From,RelationType,To},Schema)-> case { lists:member(RelationType,supported_relationships()), exist(entity,From,Schema), exist(entity,To,Schema), exist(relation,RelationType,Schema) } of {true,true,true,false}-> true; _ -> false end. %%--------------------------------------- %% Function : exist/3 %% Purpose : check the existence of an entity or a relationship in the schema %% Arg: Entity= atom(),Schema %% Return : true | false %%--------------------------------------- exist(entity,Entity,Schema)-> lists:member(Entity,fetch(entity,Schema)); exist(relation,Entity,Schema)-> lists:member(Entity,fetch(relation,Schema)). %%--------------------------------------- %% Function : /3 %% Purpose : check the existence of an entity or a relationship in the schema %% Arg: Entity= atom(),Schema %% Return : true | false %%--------------------------------------- supported_relationships()-> % [composition,dependency,reference,attribute]. [attribute,dependency,reference,composition]. %%--------------------------------------- %% Function : info/2 %% Purpose : check the schema definition %% Arg: schema,Schema %% Return : ok or wrong >reason %%--------------------------------------- %% a schema is ok if %% any relations relates existing entities %% relations are among the supported ones. append_true(true,Res)->Res; append_true(Acc,true) ->Acc; append_true(Acc,Res) ->[Res | Acc ]. is_valid(relation,{From,RelationType,To},Schema)-> lists:foldl( fun({Fun,ErrMess},Acc)-> Res=case Fun() of true->true; _ -> { ErrMess,{From,RelationType,To}} end, append_true(Acc,Res) end ,true, [{fun()-> lists:member(RelationType,supported_relationships()) end,"relationship not supported"}, {fun() ->exist(entity,From,Schema) end,"wrong source entity"}, {fun()->exist(entity,To,Schema) end,"wrong target entity"} % {fun()-> exist(relation,RelationType,Schema) end,"relation already exists"} ] ); is_valid(attribute,{From,RelationType,To},Schema)-> lists:foldl( fun({Fun,ErrMess},Acc)-> Res= case Fun() of true->true; _ -> { ErrMess,{From,RelationType,To}} end, append_true(Acc,Res) end ,true, [{fun()->case RelationType of attribute->true;_->false end end,"wrong keyword:attribute expected"}, {fun() ->exist(entity,From,Schema) end,"wrong source entity"}, {fun()->not exist(entity,To,Schema) end," entity exists with attribute name"} ] ). info(schema,Schema)-> lists:foldl(fun(R={From,RelationType,To},Acc)-> Res =case RelationType of attribute -> is_valid(attribute,R,Schema); _-> is_valid(relation,R,Schema) end, append_true(Res,Acc) end, true, fetch(relation,Schema)); %%--------------------------------------- %% Function : info/2 %% Purpose : give the table definition (like mnesia:save_to_text would) %% Arg: table,Schema %% Return : {table, [{tablename,[fieldsname]}]} %%--------------------------------------- info(tables,S)-> {tables,lists:map( fun(E)-> {E, [key |lists:map( fun({From,Rel,To})-> To end, fetch(relation,E,S))] } end , fetch(entity,S)) } ; %% Purpose : give the records definition (.hrl) %% Arg: headers,Schema %% Return : -record (tablename,[fieldsname]), string-ified. %%--------------------------------------- info(headers,S)-> {tables,T}=info(tables,S), lists:map( fun({Name,ListofFiels})-> ["-record(", atom_to_list(Name),",", io_lib:format("~p",[list_to_tuple(ListofFiels)]),").\n"] end ,T). dump_to_recordfile(File) -> dump_to_recordfile(mnesia_lib:is_running(), file:open(File, write)). dump_to_recordfile(yes, {ok, F}) -> file:write(F,list_to_binary("-module(testdata_auto).\n" "-compile([export_all]).\n" "-import(kit_db,[store/1]).\n" "-include(\"kit_schema.hrl\").\n" "test_data()->\n")), Tabs = lists:delete(schema, mnesia_lib:local_active_tables()), lists:foreach(fun(T) -> dump_tab(F, T) end, Tabs), file:write(F,list_to_binary("ok.\n") ), file:close(F); dump_to_recordfile(_,_) -> error. dump_tab(F, T) -> W = mnesia_lib:val({T, wild_pattern}), {atomic,All} = mnesia:transaction(fun() -> mnesia:match_object(W) end), R=lists:map(fun(Term) -> format_record(Term)end, All), file:write(F,list_to_binary(R)) . format_record(R)-> [T| FieldValue]=tuple_to_list(R), FieldName= mnesia_lib:val({T, attributes}), NameAndValue=lmerge(FieldName,FieldValue), ["ok=store(",$#,atom_to_list(T),${, [fr_elem(hd(NameAndValue))|fr_tail(tl(NameAndValue))], $},$),$,,$\n ]. fr_elem({Name,Value})-> [io_lib:format("~p",[Name])," = ",io_lib:format("~p",[Value])]. % [ioN,$=,V]. fr_tail([])-> ""; fr_tail([H|T]) ->[[$,,fr_elem(H)] | fr_tail(T)] . lmerge(L1,L2)-> lists:reverse(lmerge(L1,L2,[])). lmerge([],_,A)->A; lmerge(_,[],A)->A; lmerge([H1|L1],[H2|L2],Acc)-> lmerge(L1,L2, [{H1,H2}|Acc] ). %%---------------------------------------utils combine_item(L1, []) -> [L1]; combine_item(L1, L2) -> [L1]++L2. -------------- next part -------------- %% ``The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in %% compliance with the License. You should have received a copy of the %% Erlang Public License along with this software. If not, it can be %% retrieved via the world wide web at http://www.erlang.org/. %% %% Software distributed under the License is distributed on an "AS IS" %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See %% the License for the specific language governing rights and limitations %% under the License. %% %% The Initial Developer of the Original Code is luc abom. %% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings %% AB. All Rights Reserved.'' %% %% $Id: grm_mnesia.erl,v 1.11 2000/10/11 16:28:06 luc Exp $ %% -module(grm_mnesia). -author('luc@REDACTED'). -compile([export_all]). -export([info/2,info/3]). -include("kit_server.hrl"). -import(grm_schema,[fetch/3]). %-import(kit_lib,[heading/1]). -import(kit_lib_format,[for_all_sections/2]). -include("mnesia_test_lib.hrl"). %%--------------------------------------- %% Function : info/2 %% Purpose : entity | relations for a Record %% Arg: entity| relation, Record, grmSchema %% Return : entity(), relations %%--------------------------------------- info(entity, Record)-> element(1,Record). info(relation, Record, Schema) -> fetch(relation,info(entity,Record),Schema). %%--------------------------------------- %% Function : retrieve/3 %% Purpose : retrieve the records referenced by a given record %% Arg: Relation, Record | [Record], Schema %% Return : empty schema %%--------------------------------------- retrieve(Relation,Records,Schema) when list(Records)-> lists:flatten( lists:map( fun(OneRecord)-> retrieve(Relation,OneRecord,Schema) end ,Records)); retrieve(Relation,Record,Schema)-> Relations= fetch(Relation,info(entity,Record),Schema), lists:flatten( lists:map( fun({_From,_Relation,To})-> Key=field_value(Record,To), Res =retrieve_ft(To,Key,Record) end, Relations)) . %%--------------------------------------- %% Function : retrieve_ft/3 %% Purpose : retrieve the records in a table, fault tolerant manner %% Arg: Table,Keys ,FatherRecord (just for error message) %% Return : Record | [Record] %%--------------------------------------- retrieve_ft(Table, Keys,Record) when list(Keys)-> R=lists:map(fun(X)->retrieve_ft(Table,X,Record) end, Keys), K=lists:flatten(R), K ; retrieve_ft(Table,Keys,Record) when atom(Keys)-> case Res =retrieve(Table,Keys) of not_found -> kit_lib:info_report(?MODULE, [ {retrieve_ft,"key pointing wrong: ignoring\n"}, {pointing_to,Table},{key_to,Keys}, {record,Record}]), []; %fault tolerant, may be parametrable R -> [R] end. %%--------------------------------------- %% Function : retrieve/2 %% Purpose : retrieve the records in a table %% Arg: Table,Keys %% Return : Record | [Record] %%--------------------------------------- retrieve(Any, Keys) when list(Keys)-> case catch list_to_atom(Keys) of {'EXIT',_} -> lists:map(fun(X)->retrieve(Any,X) end, Keys); K -> retrieve(Any,K) end; %%not exported retrieve(Table,Key) when atom(Key)-> F = fun() -> mnesia:read({Table,Key}) end, case mnesia:transaction(F) of {atomic,[T]} -> T; Msg-> kit_lib:info_report(?MODULE, [ {retrieve_2,"record not found\n"}, {table,Table},{key,Key}, {message,Msg}]), not_found end. %%return the #Record.Field field_value(Record,Field)-> R= field_rank( info(entity,Record),Field), element(R,Record). %%Return the Rank from Field in Table field_rank(Table, Field)-> T2= mnesia:table_info(Table,attributes), find_rank(Field,T2). %%count th rank from X in L, +1 (for the record name) find_rank(X,L)-> find_rank(X,L,2). find_rank(_,[],N)->not_found; find_rank(X,[X|T],N)-> N; find_rank(X,[_|T],N) ->find_rank(X,T,N+1). %%--------------------------------------- %% Function : prototype/2 %% Purpose : create One Empty record of type Table, according to Schema %% Arg: Table,Schema %% Return : Record | [Record] %%--------------------------------------- prototype(Table, Schema)-> list_to_tuple( [Table,new_key(Table) | lists:map(fun(_)-> [] end, fetch(relation,Table,Schema))]). %%--------------------------------------- %% Function : new_key/1 %% Purpose : create a new key %% Arg: Table %% Return : Record | [Record] %%--------------------------------------- %generate a new key refering to a table new_key(Table) when atom(Table)-> {{Y,M,D},{H,Mi,S}}= erlang:universaltime(), %random:seed(),Rand=random:uniform(999), {_,_,Rand}=now(), K=io_lib:format("~w~p~p~p~p~p~p",[Y,M,D,H,Mi,S,Rand]), list_to_atom(lists:append([atom_to_list(Table) |K])). %%--------------------------------------- %% Function : add_link/2 %% Arg : Son, Father %% Purpose: add the Son Record as a link into the Father (not in the db) %% Return : FatherRecord %%--------------------------------------- add_link(Son,Father)-> SonName=info(entity,Son), Key=element(2,Son), SonRank=field_rank( info(entity,Father),SonName), CurrentValue=element(SonRank,Father), NewSource=setelement(SonRank,Father, [Key | CurrentValue ] ). %%--------------------------------------- %% Function : new_structure/2 %% Purpose : create a strucuture of new (empty) records, according to Schema %% Arg: Table,Schema %% Return : {Record, [AssociatedRecord] %%--------------------------------------- new(Table, Schema)-> new(Table, Schema,[]). new(Table, Schema,Acc2)-> {NewRec, AssociatedRecords} = lists:mapfoldl(fun({From,RelationType,To},Acc)-> new_element(RelationType,{To,Acc},Schema) end, [],fetch(relation,Table,Schema)), NK=new_key(Table), NR=list_to_tuple([Table,NK|NewRec]), {NR,[NR|lists:append([AssociatedRecords,Acc2])]}. new_element(attribute,{Relation,AccListe},Schema)-> {default(Relation), AccListe}; new_element(dependency,{Relation,AccListe},Schema) -> {default(Relation),AccListe}; new_element(reference,{To,AccListe},Schema) -> {NewRecord,AccListe2}=new(To,Schema,AccListe), {key(NewRecord), AccListe2}; new_element(composition,{To,AccListe},Schema) -> {NewRecord,AccListe2}=new(To,Schema,AccListe), {key(NewRecord), AccListe2}. default(Relation)->atom_to_list(Relation). key(R)->[element(2,R)]. %%% context related functions, to be cleaned context(append_to_heirs,Record, Context)-> ?log("Record: ~p Context : ~p",[Record,Context]), Newheirs= [[Record] | context(heirs,Context)], REsult= Context#context{ heirs=Newheirs}. context(new,Mode)-> #context{heirs=[], mode=Mode}; context(relation,Context)-> Context#context.relation; context(mode,Context)-> Context#context.mode; context(funNext,Context)-> Context#context.funNext; context(heirs,Context)-> father(Context#context.heirs); context(context,Context)->Context; context(Badarg,Context)->?log("badard:~p~n",[Badarg]). %%--------------------------------------- %% Function : father/1 %% Purpose : the father of the curernt element in a context %% Arg: [Record], = context %% Return : Record %%--------------------------------------- father([])->[]; father(Context) when list(Context)-> father(hd(Context)); father(Context)-> Context. %%initialise the navigation and thecontext navigate(Build,Record,S,Context)-> NewContext=Context#context{schema=S, funBuild=Build, allrelation= grm_schema:supported_relationships()}, next(Record,[[{composition,Record}]],NewContext). %%navigate the record nav_it(Record,Context)-> AllRec = instance_per_relation( Record,Context#context.allrelation,Context#context.schema), I3=fsg(AllRec,Context#context.schema) , %filter, sort , group next(Record,I3,Context). %go on next(Record,ListofDuplets,Context)-> %prepare next iteration NewContext1 =Context#context{ heirs= [[Record] | Context#context.heirs]}, NewContext=NewContext1#context{ funNext = fun(R1)-> nav_it(R1,NewContext1) end}, Build=NewContext#context.funBuild, Build(section,ListofDuplets,NewContext). instance_per_relation(Record,Relations,S)->%retrieve instances per relation, and build duplets lists:flatten( lists:foldl( fun(Rel,Acc)-> Rec=lists:map( fun(OneRec)-> {Rel,OneRec} end, retrieve(Rel,Record,S)), [Rec | Acc] end,[],Relations)). fsg(ListofDuplets,Schema)-> %Fiter, Sort, and groups duplets of {relation, records} I1=grm_filter(ListofDuplets), I2=grm_sort(I1,Schema), I3= group_relation_and_records(I2), %, io:format("Allrec ~p~n Sorted:noG ~p~n I3:G ~p~n",[ListofDuplets,I2,I3]), I3 . grm_filter(Allrec)-> Allrec. grm_sort(AllRec,Schema)->%sort relation first, then records OrderList=grm_schema:fetch(entity,Schema), F= fun(A,B)-> {RelA,RecA}=A, {RelB,RecB}=B, case RelA of RelB-> compare_stuff(element(1,RecA) , element(1,RecB),OrderList); _->% we ressort on ordering the relationships compare_relationships(RelA,RelB) end end, lists:sort(F,AllRec). %%--------------------------------------- %% Function : compare_relationships/2 %% Purpose : define an ordered relation on GRM relationships %% Arg: R1,R2 = supportedrelationships() %% Return : true if R1 >R2 %%--------------------------------------- compare_relationships(R1,R2)-> OrderList=[attribute,reference,composition,dependency], compare_stuff(R1,R2,OrderList). compare_stuff(R1,R2, OrderList)-> RankR1=find_rank(R1,OrderList), RankR2=find_rank(R2,OrderList), RankR1[[a,a,a],[b,b]] %% Arg: [sortedRecords], fun to compare equality of records %% Return : [[groupedRecord],..] %%--------------------------------------- group_with_key(L,Key)-> {ResList,LastCurrent}=lists:foldl( fun(Rec,Acc)->{GroupList,CurrentGroup}=Acc, case CurrentGroup of []->{GroupList,[Rec]}; H -> CurrentRecordName = Key(hd(H)), case Key(Rec) of CurrentRecordName -> %same as current {GroupList,[Rec | H ]}; _ -> %other record, new sublist {[H| GroupList ],[Rec]} end end end , {[],[]},L), lists:reverse([LastCurrent|ResList]) . group_records(L)-> F= fun(X)-> element(1,X) end, group_with_key(L,F). group_relation_and_records([])->[]; group_relation_and_records(L)-> %assuming L =[{rel,Record}] F= fun(X)->{_Rel,Rec}=X, element(1,Rec) end, group_with_key(L,F). -------------- next part -------------- %% ``The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in %% compliance with the License. You should have received a copy of the %% Erlang Public License along with this software. If not, it can be %% retrieved via the world wide web at http://www.erlang.org/. %% %% Software distributed under the License is distributed on an "AS IS" %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See %% the License for the specific language governing rights and limitations %% under the License. %% %% The Initial Developer of the Original Code is luc abom. %% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings %% AB. All Rights Reserved.'' %% %% $Id: kit_lib_format.erl,v 1.8 2000/10/11 16:28:06 luc Exp $ %% -module(kit_lib_format). -author('luc@REDACTED'). -compile([export_all]). -include("mnesia_test_lib.hrl"). -include("kit_server.hrl"). -include("kit_schema.hrl"). -import(grm_mnesia,[info/2,info/3,context/2]). -import(kit_lib,[forge_link/1, heading/1,father/1]). %-export([new/1, % add/3, % fetch/2,fetch/3, % composition/2,dependency/2,reference/2, % supported_relationships/0]). %%--------------------------------------- %% Function : grm_browse/3 %% Purpose : browse a grm schema, from a record %% Arg: relation, the record to browse, grm_schema, the Fun to naivigate the schema, the context of the current element %% Return : a formatting tree %%--------------------------------------- grm_browse(Rel,[],Context) -> kit_lib:info_report(?MODULE,[ {grm_browse_3,"received empty record: ignoring"}, {relation,Rel}, {context,Context}]), []; %%following are relation related relates grm_browse(reference,Record,Context)-> build_format(desc,Record, Context); grm_browse(composition,Record,Context)-> Next=context(funNext,Context), Next(Record); grm_browse(dependency,Record,Context) ->[]; grm_browse(attribute,Record,Context) ->[]; %%following are section related grm_browse(section,Record,Context) -> for_all_sections(Record,Context) . for_all_sections(Records,Context)-> lists:flatmap(fun(RecordSection)-> {Rel,Rec}=hd(RecordSection), case Rel of composition -> [{section, [{heading, heading(Rec)}], lists:append( [buttons_for_leafs(Rec,Context), for_all_records(RecordSection,Context)]) }]; reference ->for_all_records(RecordSection,Context) end end, Records) . %% Build all the Records for_all_records(Records,Context)-> Build=Context#context.funBuild, lists:flatmap(fun(Arg)->{Rel,Rec}=Arg, Build(Rel,Rec,Context) end, Records) . buttons_for_leafs(Record,Context)-> case context(mode,Context) of edit -> Relations=info(relation,Record,Context#context.schema), Re=lists:map( fun({From,Rel,To})->To end, Relations), R=io_lib:format("~p~p",[Re,Relations]), % io:format("R:~p~n",[R]), [{input, [{type,"submit"},{name,name_for_submit("add",Record,Context)},{value,"+"}], []}, selector(Re,name_for_submit("select",Record,Context))]; _->[] end . %%% format related functions build_format(desc,Record, Context)-> case context(mode, Context) of browse-> desc_to_para2(Record,kit_lib:forge_link(context(heirs,Context))); edit -> desc_to_input(Record); link-> link_to_para(Record); _Donk ->?log("shit mode :~p~n",[_Donk]) end . %desc(Type, L,Link) when list(L) -> % lists:flatten(lists:map(fun(X)->desc(Type,X,Link) end, L)); %desc(list, A,Link) when record(A,states) -> % desc_to_para(A#states.description,forge_link(A)); %desc(list, A,Link) when record(A,features) -> % desc_to_para(A#features.description,forge_link(A)); desc(list, A,Link) -> desc_to_para(A,Link). desc_to_para(C) when record(C,description)-> [{link, [{linkend,"testlink"}], C#description.name}, {'P', C#description.definition}]. desc_to_para(C,Link) when record(C,description) -> [{link, [{linkend,Link}], C#description.name}, {'P', C#description.definition}]. desc_to_para2(C,Link) when record(C,description) -> [{'P',[ {link, [{linkend,Link}], C#description.name}, {data, ":"}, {data,C#description.definition}]}]. desc_to_glossentry(C,Link) when record(C,description) -> [{glossentry, [{glossterm, [{link,[{linkend,Link}],C#description.name}]}, {glossdef, C#description.definition}] }]. desc_to_input(C) when record(C,description)-> [{'P',[input_field("name",C#description.key,C#description.name), input_textarea("definition",C#description.key,C#description.definition) ]}]. link_to_para( A) when record(A,menu) -> [{'P',link_to_para(A#menu.text,A#menu.link)}]. link_to_para(Text, Link) -> [{link,[{linkend,Link},{target,"main"}], Text}]. %%%%%%%%%%%%%%%%%%%% %% utils input_field(Name,Key,Value)-> {input,[{type,"text"}, {name,name_with_key(Name,Key)}, {value,Value}], ""}. input_textarea(Name,Key,Value)-> {textarea,[{name,name_with_key(Name,Key)}, {row,4},{column,70}], Value}. selector(List,Name)-> %create a list of option from a list of atoms {select,[{name,Name}], lists:map(fun(O)->{option,atom_to_list(O)} end, List) }. name_with_key(Name, Key) when atom(Key) -> %produce a NAME+_+KEY value, as string, to be used in field name identifier in forms Name++"_"++atom_to_list(Key). name_for_submit(Action,Record,Context)-> %%produce a name with the action_Table++Key Action++"_"++name_with_record(Record). name_with_record(Record)-> %%produce Table++Key atom_to_list(element(1,Record)) ++ atom_to_list(element(2,Record)). -------------- next part -------------- -module(kit_schema). -compile(export_all). schema()-> [{entity, [description,indexing,responsabilities,services,suppliers,states,invariant,component,precondition,postcondition,predicate,menu,commands,queries,dialog]}, {relation,[{component,composition,states}, {states,reference,description}, {component,reference,description}, {component,composition,commands}, {component,composition,queries}, % {features,reference,description}, % {features,composition,commands}, % {features,composition,queries}, {description, attribute,name}, {description, attribute,definition}, {menu,attribute,text}, {menu,attribute,link}, {menu,attribute,order}, {states,composition,precondition}, {states,composition,postcondition}, {precondition,reference,description}, {postcondition,reference,description}, {postcondition,composition,predicate}, {precondition,composition,predicate}, {predicate,reference,description}, {commands,reference,description}, {queries, reference,description}, {queries,composition,precondition}, {commands,composition,precondition}, {commands,composition,postcondition}, {invariant,reference,description}, {invariant,composition,predicate}, {indexing,reference,description}, {component,composition,indexing}, {responsabilities,reference,description}, {component,composition,responsabilities}, {suppliers,reference,description}, {component,composition,suppliers}, {services,reference,description}, {component,composition,services}, {dialog,reference,description}, {component,composition,dialog} ]}] . update_header()-> true =grm_schema:info(schema,schema()),%precondition F= grm_schema:info(headers,schema()), write_file("../inc/kit_schema.hrl",lists:flatten(F)). write_file(Fname,Tree)-> Pname = Fname, %"./kit.sgml", case file:write_file(Pname, list_to_binary(Tree)) of ok -> io:format(": written file: ~p~n", [Pname]); {error,Reason} -> io:format("writing mail failed, reason: ~p~n", [?MODULE,?LINE,Reason]) end. From aba3600@REDACTED Mon Oct 16 21:43:57 2000 From: aba3600@REDACTED (Andy with Recycled Electrons) Date: Mon, 16 Oct 2000 14:43:57 -0500 (CDT) Subject: Algorithmic lists In-Reply-To: Message-ID: Tell me if I'm wrong, but lazxy evaluation would screw up Real Time, right? Andy Recycled Electrons > > We (a few lunatics at AXD 301) would like to see the lists module > > support an alternative formulation of lists. > > They are indeed elegant, and Joe was correct - they're called lazy lists, > and should be implemented using zero-argument funs. To give a type > declaration of sorts: > > LazyList :: [] | [term() | fun() -> LazyList] > > Writing a module 'lazy_lists' should be a doddle. In fact, I might do it > myself. > > /Richard Carlsson > > > Richard Carlsson (richardc@REDACTED) (This space intentionally left blank.) > E-mail: Richard.Carlsson@REDACTED WWW: http://www.csd.uu.se/~richardc/ > > Sincerely, Andy Allen Recycled Electrons email: andy@REDACTED From aba3600@REDACTED Mon Oct 16 23:18:56 2000 From: aba3600@REDACTED (Andy with Recycled Electrons) Date: Mon, 16 Oct 2000 16:18:56 -0500 (CDT) Subject: Algorithmic lists In-Reply-To: Message-ID: Let me clarify... Accessing a list is a simple, real-time operation. Accessing a Lazy List may mean accessing a megapixel image from Hong Kong, where it is ray traced in true color based on several dozen light sources. In other words, there would be no gurantee of ANY timing constraints. As long as the "real time" system realizes this, everyone is happy. Also, is there a way to overload operators (not function calls) in Erlang? I'd love to overload "+" or "-" for a specific kind of input, so that a call is made to my function. The data units I am passing in are 3-element tuples right now, but I can change that. I.E., In the second half of this eMail I am searching for a way to define my own data type in Erlang and overload existing operators based on it. Does anyone have an example? Andy Recycled Electrons On Mon, 16 Oct 2000, Andy with Recycled Electrons wrote: > Date: Mon, 16 Oct 2000 14:43:57 -0500 (CDT) > From: Andy with Recycled Electrons > To: erlang-questions@REDACTED > Subject: Re: Algorithmic lists > > Tell me if I'm wrong, but lazxy evaluation would screw up Real Time, > right? > > Andy > Recycled Electrons > > > > We (a few lunatics at AXD 301) would like to see the lists module > > > support an alternative formulation of lists. > > > > They are indeed elegant, and Joe was correct - they're called lazy lists, > > and should be implemented using zero-argument funs. To give a type > > declaration of sorts: > > > > LazyList :: [] | [term() | fun() -> LazyList] > > > > Writing a module 'lazy_lists' should be a doddle. In fact, I might do it > > myself. > > > > /Richard Carlsson > > > > > > Richard Carlsson (richardc@REDACTED) (This space intentionally left blank.) > > E-mail: Richard.Carlsson@REDACTED WWW: http://www.csd.uu.se/~richardc/ > > > > > > Sincerely, > > Andy Allen > Recycled Electrons > email: andy@REDACTED > > > Sincerely, Andy Allen Recycled Electrons email: andy@REDACTED From matthias@REDACTED Mon Oct 16 23:37:55 2000 From: matthias@REDACTED (matthias@REDACTED) Date: Mon, 16 Oct 2000 23:37:55 +0200 (CEST) Subject: multiple heaps In-Reply-To: References: <39EB2B20.C09C366D@erix.ericsson.se> Message-ID: <14827.30003.138557.361916@corelatus.com> > > The separate heaps improve the soft real time characteristics of the > > emulator Miguel Barreiro Paz writes: > Now, wouldn't a shared heap with more generations achieve the same > effect? - quoting Erik Johansson: A common style of programming in Erlang involves starting many (relatively) short-lived processes which do some work which involves expanding the heap, wait for a timer to expire and then die. When that process dies, the runtime system can throw away that process' heap without having to sift through it and figure out what's used and what isn't. I.e. no marking, sweeping, tracing or similar cache-workout code. If you have many processes which execute with essentially the same code and data, for instance a call state machine, you can tweak the initial heap size to completely avoid garbage collection for all such processes. This tends to be more useful if you first make sure that the GC really is consuming a significant proportion of CPU time or memory bandwidth. Matthias From jim@REDACTED Tue Oct 17 03:32:32 2000 From: jim@REDACTED (Jim Larson) Date: Mon, 16 Oct 2000 18:32:32 -0700 Subject: Suggested Example/ emacs questions In-Reply-To: Your message of "Tue, 10 Oct 2000 13:19:46 +0200." <39E2FB52.CB8FE6E9@bluetail.com> Message-ID: <200010170132.SAA13779@eme110-115.Sendmail.COM> In message <39E2FB52.CB8FE6E9@REDACTED> Tony Rogvall writes: >Me and pekka designed a multithreaded implementation of erlang >~ two years ago (put on ice). I'd sure like to see it dusted off. When selling Erlang as an alternative to C's pthreads, it's a little embarassing to mention that there's no speedup on multiprocessor machines. Running several emulators on one machine doesn't seem adequate when the langauge semantics should allow a multiprocessor implementation with no impact on the application code. >Then we remodel the message passing and signal sending so that >a message was a sort of a signal. i.e. everything was signals. It was then >up to the receiving process (internally) to sort things out. > >One thing that I thought about then (or was it pekka) was to implement a >per process signal handler, similar to the error handler. Then this >sorting out messages from signals / exit propagation / code reloading / >suspend / resume could be implemented in erlang in a signal handler. > >What do you think about that one ??? One of the things that impresses me about Erlang is that the inevitable tensions between purity, simplicity, and elegance on the one hand, and the immediate needs of large-scale, commercial products on the other, have always been met in a practical compromise. I think that Erlang needs to be as wary of "Creeping Elegance" as of "Creeping Features". For instance, built-in support for ETS tables seems to be the Right Thing. Henry Baker's essay http://ftp.netcom.com/pub/hb/hbaker/TreeShadow.html (the link may not work - Netcom's site seems to have gone away - anyone know where it's gone to these days?) makes the interesting point that large-scale digital hardware designs aren't purely functional - they are structured as stateful registers with functional logic as glue between them. Erlang's tables, modules, and messages fill the same role. I've always had mixed feelings about the loss of vanilla Erlang message-passing in an OTP environment. However, I've still gotten most of my work done with generic behaviours. The signals proposal above seems isomorphic to mapping everything onto Erlang messages, but creating a system-level receive loop. I'd like to see ideas about generalizing this mechanism so that application programmers could use it. However, I'll also guess that going down that path will lead to "Creeping Elegance". Ambivalently Yours, Jim From etxuwig@REDACTED Mon Oct 16 17:04:12 2000 From: etxuwig@REDACTED (Ulf Wiger) Date: Mon, 16 Oct 2000 17:04:12 +0200 (MET DST) Subject: Algorithmic lists In-Reply-To: Message-ID: Here's my first (and perhaps last) hack of sys_pre_expand.erl. It handles at least my very simple test function with a list comprehension that is fed a lazy list: ========================= -module(alglists). -author('etxuwig@REDACTED'). -export([seq/2, foreach/2, foo/0]). seq(Start, Stop) when Start =< Stop -> [Start | fun() -> seq(Start+1, Stop) end]; seq(Start, Stop) -> []. foreach(F, []) -> ok; foreach(F, [H|T]) when list(T) -> F(H), foreach(F, T); foreach(F, [H|T]) when function(T) -> F(H), foreach(F, T(H, T)). foo() -> [X || X <- seq(1,5), X > 3]. ========================= Eshell V5.0.1 (abort with ^G) 1> c(alglists). {ok,alglists} 2> alglists:seq(1,5). [1|#Fun] 3> alglists:foo(). [4,5] Unfortunately, it doesn't address LCs in the shell (that's done in erl_eval.erl). There is at least one really ugly hack in there: etxuwig@REDACTED > diff .../otp_src_R7A-0/lib/compiler/src/sys_pre_expand.erl sys_pre_expand.erl 443c443,447 < {Lc,St3} = lc_tq(Line, E, Qs, NewMore, St2), --- > NewMoreFun = {call,Lg,Fun,[{call,Lg,Tail,[]},Fun]}, > IsList = [{call,Lg,{atom,Lg,list},[Tail]}], > IsFun = [{call,Lg,{atom,Lg,function},[Tail]}], > {LcL,_} = lc_tq(Line, E, Qs, NewMore, St2), > {LcF,St3} = lc_tq(Line, E, Qs, NewMoreFun, St2), 447,448c451,456 < {clauses,[{clause,Lg,[{cons,Lg,P,Tail},Fun],[],[Lc]}, < {clause,Lg,[{cons,Lg,{var,Lg,'_'},Tail},Fun],[],[NewMore]}, --- > {clauses,[{clause,Lg,[{cons,Lg,P,Tail},Fun],[IsList],[LcL]}, > {clause,Lg,[{cons,Lg,P,Tail},Fun],[IsFun],[LcF]}, > {clause,Lg,[{cons,Lg,{var,Lg,'_'},Tail},Fun], > [IsList],[NewMore]}, > {clause,Lg,[{cons,Lg,{var,Lg,'_'},Tail},Fun], > [IsFun],[NewMoreFun]}, On 16 Oct 2000, Bjorn Gustavsson wrote: >We don't have any free tags to add a new cons-like type. A new type >would need a header word indicating the type and size, and would >therefore not be smaller than a tuple. > >We already use non-normalised lists in Erlang and OTP. The >list_to_binary/1 BIF and ports accept binaries in list tails. Personally, I think it's more intuitive to stay close to the list syntax. After all, we are looking to extend the list semantics. /Uffe -- Ulf Wiger tfn: +46 8 719 81 95 Strategic Product & System Management mob: +46 70 519 81 95 Ericsson Telecom AB, Datacom Networks and IP Services Varuv?gen 9, ?lvsj?, S-126 25 Stockholm, Sweden -------------- next part -------------- %% ``The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in %% compliance with the License. You should have received a copy of the %% Erlang Public License along with this software. If not, it can be %% retrieved via the world wide web at http://www.erlang.org/. %% %% Software distributed under the License is distributed on an "AS IS" %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See %% the License for the specific language governing rights and limitations %% under the License. %% %% The Initial Developer of the Original Code is Ericsson Utvecklings AB. %% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings %% AB. All Rights Reserved.'' %% %% $Id$ %% %% Purpose : Expand some source Erlang constructions. This is part of the %% pre-processing phase. %% N.B. Although structs (tagged tuples) are not yet allowed in the %% language there is code included in pattern/2 and expr/3 (commented out) %% that handles them by transforming them to tuples. -module(sys_pre_expand). %% Main entry point. -export([module/2]). -import(ordsets, [list_to_set/1,add_element/2, union/1,union/2,intersection/1,intersection/2,subtract/2]). -import(lists, [member/2,map/2,foldl/3,foldr/3]). -include("../include/erl_bits.hrl"). -record(expand, {module=[], %Module name exports=[], %Exports imports=[], %Imports compile=[], %Compile flags records=dict:new(), %Record definitions attributes=[], %Attributes defined=[], %Defined functions vcount=0, %Variable counter func=[], %Current function bitdefault, bittypes }). %% module(Forms, CompileOptions) %% {ModuleName,Exports,TransformedForms} %% Expand the forms in one module. N.B. the lists of predefined functions %% and exports are really ordsets! module(Fs, Opts) -> %% Set pre-defined functions. PreDef = [{module_info,0},{module_info,1}], %% Set pre-defined exports. PreExp = [{module_info,0},{module_info,1}], %% Build initial expand record. St0 = #expand{exports=PreExp, compile=Opts, defined=PreDef, bitdefault = erl_bits:system_bitdefault(), bittypes = erl_bits:system_bittypes() }, %% Expand the functions. {Tfs,St1} = forms(Fs, foldl(fun define_function/2, St0, Fs)), %% Get the correct list of exported functions. Exports = case member(export_all, St1#expand.compile) of true -> St1#expand.defined; false -> St1#expand.exports end, %% Generate compile time info. {{Y,Mo,D}, {H,Mi,S}} = erlang:universaltime(), Compile = [{time,{Y,Mo,D,H,Mi,S}},{options,St1#expand.compile}], %% Generate all functions from stored info. {Ats,St2} = module_attrs(Exports, Compile, St1), {Mfs,St3} = module_info_funcs(Exports, Compile, St2), {St3#expand.module,Exports,Ats ++ Tfs ++ Mfs,St3#expand.compile}. %% -type define_function(Form, State) -> State. %% Add function to defined if form a function. define_function({attribute,L,asm,{function,N,A,Code}}, St) -> St#expand{defined=add_element({N,A}, St#expand.defined)}; define_function({function,L,N,A,Cs}, St) -> St#expand{defined=add_element({N,A}, St#expand.defined)}; define_function(F, St) -> St. module_attrs(Exp, Compile, St) -> {[{attribute,0,Name,Val} || {Name,Val} <- St#expand.attributes],St}. module_info_funcs(Exp, Compile, St) -> {[{function,0,module_info,0, [{clause,0,[],[], [{nil,0}]}]}, {function,0,module_info,1, [{clause,0,[{var,0,'_'}],[], [{nil,0}]}]}], St}. %% forms(Forms, State) -> %% {TransformedForms,State'} %% Process the forms. Attributes are lost and just affect the state. %% Asm things are funny, just pass them through. Ignore uninteresting forms %% like eof and type. forms([{attribute,L,asm,{function,N,A,Code}}|Fs0], St0) -> {Fs,St1} = forms(Fs0, St0), {[{asm,N,A,Code}|Fs],St1}; forms([{attribute,L,Name,Val}|Fs0], St0) -> St1 = attribute(L, Name, Val, St0), forms(Fs0, St1); forms([{function,L,N,A,Cs}|Fs0], St0) -> {Ff,St1} = function(L, N, A, Cs, St0), {Fs,St2} = forms(Fs0, St1), {[Ff|Fs],St2}; forms([F|Fs], St) -> forms(Fs, St); forms([], St) -> {[],St}. %% -type attribute(Line, Attribute, Value, State) -> %% State. %% Process an attribute, this just affects the state. attribute(L, module, Module, St) -> St#expand{module=Module}; attribute(L, export, Es, St) -> St#expand{exports=union(list_to_set(Es), St#expand.exports)}; attribute(L, import, Is, St) -> import(L, Is, St); attribute(L, compile, C, St) when list(C) -> St#expand{compile=St#expand.compile ++ C}; attribute(L, compile, C, St) -> St#expand{compile=St#expand.compile ++ [C]}; attribute(L, record, {Name,Defs}, St) -> St#expand{records=dict:store(Name, normalise_fields(Defs), St#expand.records)}; attribute(L, file, File, St) -> St; %This is ignored attribute(L, Name, Val, St) when list(Val) -> St#expand{attributes=St#expand.attributes ++ [{Name,Val}]}; attribute(L, Name, Val, St) -> St#expand{attributes=St#expand.attributes ++ [{Name,[Val]}]}. function(L, N, A, Cs0, St0) -> {Cs,St} = clauses(Cs0, St0#expand{func=N}), {{function,L,N,A,Cs},St}. %% -type clauses([Clause], State) -> %% {[TransformedClause],State}. %% Expand function clauses. clauses([{clause,Line,H0,G0,B0}|Cs0], St0) -> {H,Hvs,Hus,St1} = head(H0, St0), {G,Gvs,Gus,St2} = guard(G0, Hvs, St1), {B,Bvs,Bus,St3} = exprs(B0, union(Hvs, Gvs), St2), {Cs,St4} = clauses(Cs0, St3), {[{clause,Line,H,G,B}|Cs],St4}; clauses([], St) -> {[],St}. %% head(HeadPatterns, State) -> %% {TransformedPatterns,Variables,UsedVariables,State'} head(As, St) -> pattern_list(As, St). %% pattern(Pattern, State) -> %% {TransformedPattern,Variables,UsedVariables,State'} %% BITS: added used variables for bit patterns with varaible length %% pattern({var,Line,'_'}=Var, St) -> %Ignore anonymous variable. {Var,[],[],St}; pattern({var,Line,V}=Var, St) -> {Var,[V],[],St}; pattern({integer,Line,I}=Int, St) -> {Int,[],[],St}; pattern({float,Line,F}=Float, St) -> {Float,[],[],St}; pattern({atom,Line,A}=Atom, St) -> {Atom,[],[],St}; pattern({string,Line,S}=String, St) -> {String,[],[],St}; pattern({nil,Line}=Nil, St) -> {Nil,[],[],St}; pattern({cons,Line,H,T}, St0) -> {TH,THvs,Hus,St1} = pattern(H, St0), {TT,TTvs,Tus,St2} = pattern(T, St1), {{cons,Line,TH,TT},union(THvs, TTvs),union(Hus,Tus),St2}; pattern({tuple,Line,Ps}, St0) -> {TPs,TPsvs,Tus,St1} = pattern_list(Ps, St0), {{tuple,Line,TPs},TPsvs,Tus,St1}; %%pattern({struct,Line,Tag,Ps}, St0) -> %% {TPs,TPsvs,St1} = pattern_list(Ps, St0), %% {{tuple,Line,[{atom,Line,Tag}|TPs]},TPsvs,St1}; pattern({record_index,Line,Name,Field}, St) -> {index_expr(Line, Field, Name, record_fields(Name, St)),[],[],St}; pattern({record,Line,Name,Pfs}, St0) -> Fs = record_fields(Name, St0), {TMs,TMsvs,Us,St1} = pattern_list(pattern_fields(Fs, Pfs), St0), {{tuple,Line,[{atom,Line,Name}|TMs]},TMsvs,Us,St1}; pattern({bin,Line,Es0}, St0) -> {Es1,Esvs,Esus,St1} = pattern_bin(Es0, St0), {{bin,Line,Es1},Esvs,Esus,St1}; pattern({op,Line,'++',{nil,_},R}, St) -> pattern(R, St); pattern({op,Line,'++',{cons,Li,H,T},R}, St) -> pattern({cons,Li,H,{op,Li,'++',T,R}}, St); pattern({op,Line,'++',{string,Li,L},R}, St) -> pattern(string_to_conses(Li, L, R), St); pattern({match,Line,Pat1, Pat2}, St0) -> {TH,Hvt,Hus,St1} = pattern(Pat2, St0), {TT,Tvt,Tus,St2} = pattern(Pat1, St1), {{match,Line,TT,TH}, union(Hvt,Tvt), union(Hus,Tus), St2}; %% Compile-time pattern expressions, including unary operators. pattern({op,Line,Op,A}, St) -> { erl_eval:partial_eval({op,Line,Op,A}), [], [], St}; pattern({op,Line,Op,L,R}, St) -> { erl_eval:partial_eval({op,Line,Op,L,R}), [], [], St}. pattern_list([P0|Ps0], St0) -> {P,Pvs,Pus,St1} = pattern(P0, St0), {Ps,Psvs,Psus,St2} = pattern_list(Ps0, St1), {[P|Ps],union(Pvs, Psvs),union(Pus, Psus),St2}; pattern_list([], St) -> {[],[],[],St}. %% guard(Guard, VisibleVariables, State) -> %% {TransformedGuard,NewVariables,UsedVariables,State'} %% Transform a list of guard tests. We KNOW that this has been checked %% and what the guards test are. Use expr for transforming the guard %% expressions. guard([G0|Gs0], Vs, St0) -> {G,Hvs,Hus,St1} = guard_tests(G0, Vs, St0), {Gs,Tvs,Tus,St2} = guard(Gs0, Vs, St1), {[G|Gs],union(Hvs, Tvs),union(Hus, Tus),St2}; guard([], Vs, St) -> {[],[],[],St}. guard_tests([{op,Line,Op,L0,R0}|Gs0], Vs, St0) -> {L,Lvs,Lus,St1} = expr(L0, Vs, St0), {R,Rvs,Rus,St2} = expr(R0, Vs, St1), {Gs,Gsvs,Gsus,St3} = guard_tests(Gs0, union(Lvs, Rvs), St2), {[{op,Line,Op,L,R}|Gs],union([Lvs,Rvs,Gsvs]),union([Lus,Rus,Gsus]),St3}; guard_tests([{call,Line,{atom,Lr,record},[A,{atom,Ln,Name}]}|Gs], Vs, St) -> Fs = record_fields(Name, St), guard_tests([{op,Line,'==',{call,Line,{atom,Ln,size},[A]}, {integer,Ln,length(Fs)+1}}, {op,Line,'==',{call,Line,{atom,Ln,element},[{integer,Ln,1},A]}, {atom,Ln,Name}}|Gs], Vs, St); guard_tests([{call,Line,Test,As0}|Gs0], Vs, St0) -> {As,Asvs,Asus,St1} = expr_list(As0, Vs, St0), {Gs,Gsvs,Gsus,St2} = guard_tests(Gs0, union(Asvs, Vs), St1), TestF = {remote,Line,{atom,Line,erlang},Test}, {[{call,Line,TestF,As}|Gs],union(Asvs, Gsvs),union(Asus, Gsus),St2}; guard_tests([{atom,Line,true}|Gs], Vs, St) -> guard_tests(Gs, Vs, St); guard_tests([], Vs, St) -> {[],[],[],St}. %% exprs(Expressions, VisibleVariables, State) -> %% {TransformedExprs,NewVariables,UsedVariables,State'} exprs([E0|Es0], Vs, St0) -> {E,Evs,Eus,St1} = expr(E0, Vs, St0), {Es,Esvs,Esus,St2} = exprs(Es0, union(Evs, Vs), St1), {[E|Es],union(Evs, Esvs),union(Eus, Esus),St2}; exprs([], Vs, St) -> {[],[],[],St}. %% expr(Expression, VisibleVariables, State) -> %% {TransformedExpression,NewVariables,UsedVariables,State'} expr({var,Line,V}, Vs, St) -> {{var,Line,V},[],[V],St}; expr({integer,Line,I}, Vs, St) -> {{integer,Line,I},[],[],St}; expr({float,Line,F}, Vs, St) -> {{float,Line,F},[],[],St}; expr({atom,Line,A}, Vs, St) -> {{atom,Line,A},[],[],St}; expr({string,Line,S}, Vs, St) -> {{string,Line,S},[],[],St}; expr({nil,Line}, Vs, St) -> {{nil,Line},[],[],St}; expr({cons,Line,H0,T0}, Vs, St0) -> {H,Hvs,Hus,St1} = expr(H0, Vs, St0), {T,Tvs,Tus,St2} = expr(T0, Vs, St1), {{cons,Line,H,T},union(Hvs, Tvs),union(Hus, Tus),St2}; expr({lc,Line,E,Qs}, Vs, St) -> lc_tq(Line, E, Qs, {nil,Line}, Vs, St); expr({tuple,Line,Es0}, Vs, St0) -> {Es1,Esvs,Esus,St1} = expr_list(Es0, Vs, St0), {{tuple,Line,Es1},Esvs,Esus,St1}; %%expr({struct,Line,Tag,Es0}, Vs, St0) -> %% {Es1,Esvs,Esus,St1} = expr_list(Es0, Vs, St0), %% {{tuple,Line,[{atom,Line,Tag}|Es1]},Esvs,Esus,St1}; expr({record_index,Line,Name,F}, Vs, St) -> I = index_expr(Line, F, Name, record_fields(Name, St)), expr(I, Vs, St); expr({record,Line,Name,Is}, Vs, St) -> expr({tuple,Line,[{atom,Line,Name}| record_inits(record_fields(Name, St), Is)]}, Vs, St); expr({record_field,Line,R,Name,F}, Vs, St) -> I = index_expr(Line, F, Name, record_fields(Name, St)), expr({call,Line,{atom,Line,element},[I,R]}, Vs, St); expr({record,Line,R,Name,Us}, Vs, St0) -> {Ue,St1} = record_update(R, Name, record_fields(Name, St0), Us, St0), expr(Ue, Vs, St1); expr({bin,Line,Es0}, Vs, St0) -> {Es1,Esvs,Esus,St1} = expr_bin(Es0, Vs, St0), {{bin,Line,Es1},Esvs,Esus,St1}; expr({block,Line,Es0}, Vs, St0) -> {Es,Esvs,Esus,St1} = exprs(Es0, Vs, St0), {{block,Line,Es},Esvs,Esus,St1}; expr({'if',Line,Cs0}, Vs, St0) -> {Cs,Csvss,Csuss,St1} = icr_clauses(Cs0, Vs, St0), {All,Some} = new_in(Vs, Csvss), {{'if',Line,Cs},All,union(Csuss),St1}; expr({'case',Line,E0,Cs0}, Vs, St0) -> {E,Evs,Eus,St1} = expr(E0, Vs, St0), {Cs,Csvss,Csuss,St2} = icr_clauses(Cs0, union(Evs, Vs), St1), {All,Some} = new_in(Vs, Csvss), {{'case',Line,E,Cs},union(Evs, All),union([Eus|Csuss]),St2}; expr({'receive',Line,Cs0}, Vs, St0) -> {Cs,Csvss,Csuss,St1} = icr_clauses(Cs0, Vs, St0), {All,Some} = new_in(Vs, Csvss), {{'receive',Line,Cs},All,union(Csuss),St1}; expr({'receive',Line,Cs0,To0,ToEs0}, Vs, St0) -> {To,Tovs,Tous,St1} = expr(To0, Vs, St0), {ToEs,ToEsvs,ToEsus,St2} = exprs(ToEs0, Vs, St1), {Cs,Csvss,Csuss,St3} = icr_clauses(Cs0, Vs, St2), {All,Some} = new_in(Vs, [ToEsvs|Csvss]), {{'receive',Line,Cs,To,ToEs},union(Tovs, All),union([Tous|Csuss]),St3}; expr({'fun',Line,Body}, Vs, St) -> fun_tq(Line, Body, Vs, St); expr({call,Line,{atom,La,apply},As0}, Vs, St0) when length(As0) == 2 -> {As,Asvs,Asus,St1} = expr_list(As0, Vs, St0), {{call,Line,{remote,La,{atom,La,erlang},{atom,La,apply}},As},Asvs,Asus,St1}; expr({call,Line,{atom,La,record_info},[{atom,Li,Info},{atom,Ln,Name}]}, Vs, St) -> case Info of size -> {{integer,Line,1+length(record_fields(Name, St))},[],[],St}; fields -> {make_list(field_names(record_fields(Name, St)), Line), [],[],St} end; expr({call,Line,{atom,La,N},As0}, Vs, St0) -> {As,Asvs,Asus,St1} = expr_list(As0, Vs, St0), Ar = length(As), case erl_internal:bif(N, Ar) of true -> {{call,Line,{remote,La,{atom,La,erlang},{atom,La,N}},As}, Asvs,Asus,St1}; false -> case imported(N, Ar, St1) of {yes,Mod} -> {{call,Line,{remote,La,{atom,La,Mod},{atom,La,N}},As}, Asvs,Asus,St1}; no -> {{call,Line,{atom,La,N},As},Asvs,Asus,St1} end end; expr({call,Line,{remote,Lr,{atom,Lm,M},{atom,Lf,F}},As0}, Vs, St0) -> {As1,Asvs,Asus,St1} = expr_list(As0, Vs, St0), {{call,Line,{remote,Lr,{atom,Lm,M},{atom,Lf,F}},As1},Asvs,Asus,St1}; expr({call,Line,{remote,Lr,M,F},As}, Vs, St) -> expr({call,Line,{atom,Line,apply},[M,F,make_list(As, Line)]}, Vs, St); expr({call,Line,F,As0}, Vs, St0) -> {[Fun1|As1],Asvs,Asus,St1} = expr_list([F|As0], Vs, St0), {{call,Line,Fun1,As1},Asvs,Asus,St1}; expr({'catch',Line,E0}, Vs, St0) -> %% Catch exports no new variables. {E,Evs,Eus,St1} = expr(E0, Vs, St0), {{'catch',Line,E},[],Eus,St1}; expr({match,Line,P0,E0}, Vs, St0) -> {E,Evs,Eus,St1} = expr(E0, Vs, St0), {P,Pvs,Pus,St2} = pattern(P0, St1), {{match,Line,P,E}, union(subtract(Pvs, Vs), Evs), union(intersection(Pvs, Vs), union(Eus,Pus)),St2}; expr({op,Line,'++',{lc,Ll,E,Qs},L2}, Vs, St) -> lc_tq(Ll, E, Qs, L2, Vs, St); expr({op,Ll,'++',{string,L1,S1},{string,L2,S2}}, Vs, St) -> {{string,L1,S1 ++ S2},[],[],St}; expr({op,Ll,'++',{string,L1,S1},R0}, Vs, St0) -> {R1,Rvs,Rus,St1} = expr(R0, Vs, St0), E = case R1 of {string,L2,S2} -> {string,L1,S1 ++ S2}; Other -> string_to_conses(L1, S1, R1) end, {E,Rvs,Rus,St1}; expr({op,Ll,'++',{cons,Lc,H,T},L2}, Vs, St) -> expr({cons,Ll,H,{op,Lc,'++',T,L2}}, Vs, St); expr({op,Ll,'++',{nil,Ln},L2}, Vs, St) -> expr(L2, Vs, St); expr({op,Line,Op,A0}, Vs, St0) -> {A,Avs,Aus,St1} = expr(A0, Vs, St0), {{op,Line,Op,A},Avs,Aus,St1}; expr({op,Line,Op,L0,R0}, Vs, St0) -> {L,Lvs,Lus,St1} = expr(L0, Vs, St0), {R,Rvs,Rus,St2} = expr(R0, Vs, St1), {{op,Line,Op,L,R},union(Lvs, Rvs),union(Lus, Rus),St2}. expr_list([E0|Es0], Vs, St0) -> {E,Evs,Eus,St1} = expr(E0, Vs, St0), {Es,Esvs,Esus,St2} = expr_list(Es0, Vs, St1), {[E|Es],union(Evs, Esvs),union(Eus, Esus),St2}; expr_list([], Vs, St) -> {[],[],[],St}. %% icr_clauses([Clause], [VisibleVariable], State) -> %% {[TransformedClause],[[NewVariable]],[[UsedVariable]],State'} %% Be very careful here to return the variables that are really used %% and really new. icr_clauses([{clause,Line,H0,G0,B0}|Cs0], Vs, St0) -> {H,Hvs,Hus,St1} = head(H0, St0), %Hvs is really used! {G,Gvs,Gus,St2} = guard(G0, union(Hvs, Vs), St1), {B,Bvs,Bus,St3} = exprs(B0, union([Vs,Hvs,Gvs]), St2), New = subtract(union([Hvs,Gvs,Bvs]), Vs), %Really new Used = intersection(union([Hvs,Hus,Gus,Bus]), Vs), %Really used {Cs,Csvs,Csus,St4} = icr_clauses(Cs0, Vs, St3), {[{clause,Line,H,G,B}|Cs],[New|Csvs],[Used|Csus],St4}; icr_clauses([], Vs, St) -> {[],[],[],St}. %% lc_tq(Line, Expr, Qualifiers, More, [VisibleVar], State) -> %% {ListComp,[NewVar],[UsedVar],State'} %% %% This TQ from Simon PJ pp 127-138. No need to call ourselves %% recursively on append as this is special cased in expr as an %% optimisation. As Erlang doesn't have letrec's we explicitly pass %% the lambda as an extra argument to be able to do the recursive %% call. We also do the argument matching in the head of the lambda %% instead of in a 'case' so as to automatically get the correct %% shadowing. lc_tq(Line, E, Qs, More, Vs, St0) -> {Lc,St1} = lc_tq(Line, E, Qs, More, St0), expr(Lc, Vs, St1). lc_tq(Line, E, [{generate,Lg,P,G}|Qs], More, St0) -> {Fun,St1} = new_var(Lg, St0), {Tail,St2} = new_var(Lg, St1), NewMore = {call,Lg,Fun,[Tail,Fun]}, NewMoreFun = {call,Lg,Fun,[{call,Lg,Tail,[]},Fun]}, IsList = [{call,Lg,{atom,Lg,list},[Tail]}], IsFun = [{call,Lg,{atom,Lg,function},[Tail]}], {LcL,_} = lc_tq(Line, E, Qs, NewMore, St2), {LcF,St3} = lc_tq(Line, E, Qs, NewMoreFun, St2), {{block,Lg, [{match,Lg,Fun, {'fun',Lg, {clauses,[{clause,Lg,[{cons,Lg,P,Tail},Fun],[IsList],[LcL]}, {clause,Lg,[{cons,Lg,P,Tail},Fun],[IsFun],[LcF]}, {clause,Lg,[{cons,Lg,{var,Lg,'_'},Tail},Fun], [IsList],[NewMore]}, {clause,Lg,[{cons,Lg,{var,Lg,'_'},Tail},Fun], [IsFun],[NewMoreFun]}, {clause,Lg,[{nil,Lg},Fun],[],[More]}]}}}, {call,Lg,Fun,[G,Fun]}]}, St3}; lc_tq(Line, E, [F|Qs], More, St0) -> Lf = element(2, F), {Lc,St1} = lc_tq(Line, E, Qs, More, St0), case erl_lint:is_guard_test(F) of true -> {{'if',Lf, [{clause,Lf,[],[[F]],[Lc]}, {clause,Lf,[],[],[More]}]},St1}; false -> case F of {op,_,'not',F1} -> %Get rid of not operator. {{'case',Lf,F1, [{clause,Lf,[{atom,Lf,false}],[],[Lc]}, {clause,Lf,[{atom,Lf,true}],[],[More]}]},St1}; Other -> {{'case',Lf,F, [{clause,Lf,[{atom,Lf,true}],[],[Lc]}, {clause,Lf,[{atom,Lf,false}],[],[More]}]},St1} end end; lc_tq(Line, E, [], More, St) -> {{cons,Line,E,More},St}. %% fun_tq(Line, Body, VisibleVariables, State) -> %% {Fun,NewVariables,UsedVariables,State'} %% Transform a fun into into a tuple {'fun',Mod,I,Uniq,Free} and add %% clauses to module_lambdas/4 to hold the code. Process the body %% sequence directly to get the new and used variables, this also %% allows recursives call for the function case to correctly handle %% BIFs. N.B. erl_lint disallows imports so we don't have to check. fun_tq(Lf, {function,F,A}, Vs, St0) -> %% Core {{'fun',Lf,{function,F,A}},[],[],St0} {As,St1} = new_vars(A, Lf, St0), fun_tq(Lf, {clauses,[{clause,Lf,As,[],[{call,Lf,{atom,Lf,F},As}]}]}, Vs, St1); fun_tq(Lf, {clauses,Cs0}, Vs, St0) -> Uniq = erlang:hash(Cs0, (1 bsl 27)-1), %% The added clauses are transformed directly to get %% new/used vars. The line number hack is to get the same %% indentifier in both cases. This SUCKS! {Cs1,Hvss,Frees,St1} = fun_clauses(Cs0, Vs, St0), Ufrees = union(Frees), Free = intersection(Ufrees, Vs), {{'fun',Lf,{clauses,Cs1},{Uniq,Hvss,Free}},[],Ufrees,St1}. fun_clauses([{clause,L,H0,G0,B0}|Cs0], Vs, St0) -> {H,Hvs,Hus,St1} = head(H0, St0), {G,Gvs,Gus,St2} = guard(G0, union(Hvs, Vs), St1), {B,Bvs,Bus,St3} = exprs(B0, union([Vs,Hvs,Gvs]), St2), %% Free variables cannot be new anywhere in the clause. Free = subtract(union([Gus,Hus,Bus]), union([Hvs,Gvs,Bvs])), %%io:format(" Gus :~p~n Bvs :~p~n Bus :~p~n Free:~p~n" ,[Gus,Bvs,Bus,Free]), {Cs,Hvss,Frees,St4} = fun_clauses(Cs0, Vs, St3), {[{clause,L,H,G,B}|Cs],[Hvs|Hvss],[Free|Frees],St4}; fun_clauses([], Vs, St) -> {[],[],[],St}. %% normalise_fields([RecDef]) -> [Field]. %% Normalise the field definitions to always have a default value. If %% none has been given then use 'undefined'. normalise_fields(Fs) -> map(fun ({record_field,Lf,Field}) -> {record_field,Lf,Field,{atom,Lf,undefined}}; (F) -> F end, Fs). %% record_fields(RecordName, State) %% find_field(FieldName, Fields) record_fields(R, St) -> dict:fetch(R, St#expand.records). find_field(F, [{record_field,Lf,{atom,La,F},Val}|Fs]) -> {ok,Val}; find_field(F, [_|Fs]) -> find_field(F, Fs); find_field(F, []) -> error. %% field_names(RecFields) -> [Name]. %% Return a list of the field names structures. field_names(Fs) -> map(fun ({record_field,Lf,Field,Val}) -> Field end, Fs). %% index_expr(Line, FieldExpr, Name, Fields) -> IndexExpr. %% Return an expression which evaluates to the index of a %% field. Currently only handle the case where the field is an %% atom. This expansion must be passed through expr again. index_expr(Line, {atom,La,F}, Name, Fs) -> {integer,Line,index_expr(F, Fs, 2)}. index_expr(F, [{record_field,Lf,{atom,La,F},Val}|Fs], I) -> I; index_expr(F, [_|Fs], I) -> index_expr(F, Fs, I+1). %% pattern_fields([RecDefField], [Match]) -> [Pattern]. %% Build a list of match patterns for the record tuple elements. %% This expansion must be passed through pattern again. N.B. We are %% scanning the record definition field list! pattern_fields(Fs, Ms) -> map(fun ({record_field,L,{atom,La,F},D}) -> case find_field(F, Ms) of {ok,Match} -> Match; error -> {var,L,'_'} end end, Fs). %% record_inits([RecDefField], [Init]) -> [InitExpr]. %% Build a list of initialisation expressions for the record tuple %% elements. This expansion must be passed through expr %% again. N.B. We are scanning the record definition field list! record_inits(Fs, Is) -> map(fun ({record_field,L,{atom,La,F},D}) -> case find_field(F, Is) of {ok,Init} -> Init; error -> D end end, Fs). %% record_update(Record, RecordName, [RecDefField], [Update], State) -> %% {Expr,State'} %% Build an expression to update fields in a record returning a new %% record. Try to be smart and optimise this. This expansion must be %% passed through expr again. record_update(R, Name, Fs, Us, St) -> Nf = length(Fs), %# of record fields Nu = length(Us), %# of update fields Nc = Nf - Nu, %# of copy fields %% Try to be intelligent about which method of updating record to use. if Nu == 0 -> {record_el(R, Name, Fs, Us),St}; Nu == 1 -> {record_setel(R, Name, Fs, Us),St}; Nc == 1 -> {record_el(R, Name, Fs, Us),St}; true -> record_match(R, Name, Fs, Us, St) end. %% record_match(Record, RecordName, [RecDefField], [Update], State) %% Build a 'case' expression to modify record fields. record_match(R, Name, Fs, Us, St0) -> {Ps,News,St1} = record_upd_fs(Fs, Us, St0), Lr = element(2, hd(Us)), {{'case',Lr,R, [{clause,Lr,[{tuple,Lr,[{atom,Lr,Name}|Ps]}],[], [{tuple,Lr,[{atom,Lr,Name}|News]}]}, {clause,Lr,[{var,Lr,'_'}],[], [call_fault(Lr, {tuple,Lr,[{atom,Lr,badrecord},{atom,Lr,Name}]})]} ]}, St1}. record_upd_fs([{record_field,Lf,{atom,La,F},Val}|Fs], Us, St0) -> case find_field(F, Us) of {ok,New} -> {Ps,News,St1} = record_upd_fs(Fs, Us, St0), {[{var,Lf,'_'}|Ps],[New|News],St1}; error -> {P,St1} = new_var(Lf, St0), {Ps,News,St2} = record_upd_fs(Fs, Us, St1), {[P|Ps],[P|News],St2} end; record_upd_fs([], Us, St) -> {[],[],St}. %% record_el(Record, RecordName, [RecDefField], [Update]) %% Build a new record using Rec#name.field expression for unchanged %% values. record_el(R, Name, Fs, []) -> record_el1(R, Name, Fs, [], element(2, R)); record_el(R, Name, Fs, Us) -> record_el1(R, Name, Fs, Us, element(2, hd(Us))). record_el1(R, Name, Fs, Us, Lr) -> {tuple,Lr,[{atom,Lr,Name}| map(fun (F) -> record_el2(R, Name, F, Us) end, Fs)]}. record_el2(R, Name, {record_field,Lf,{atom,La,F},Val}, Us) -> case find_field(F, Us) of {ok,New} -> New; error -> {record_field,Lf,R,Name,{atom,La,F}} end. %% record_setel(Record, RecordName, [RecDefField], [Update]) %% Build a nested chain of setelement calls to build updated record tuple. record_setel(R, Name, Fs, Us) -> foldr(fun ({record_field,Lf,Field,Val}, Acc) -> I = index_expr(Lf, Field, Name, Fs), {call,Lf,{atom,Lf,setelement},[I,Acc,Val]} end, R, Us). %% pattern_bin([Element], State) -> {[Element],[Variable],[UsedVar],State}. pattern_bin(Es0, St) -> Es1 = bin_expand_strings(Es0), foldr(fun (E, Acc) -> pattern_element(E, Acc) end, {[],[],[],St}, Es1). pattern_element({bin_element,Line,Expr,Size,Type}, {Es,Esvs,Esus,St0}) -> {Expr1,Vs1,Us1,St1} = pattern(Expr, St0), {Size1,Vs2,Us2,St2} = pat_bit_size(Size, St1), {Size2,Type1} = make_bit_type(Line, Size1,Type), {[{bin_element,Line,Expr1,Size2,Type1}|Es], union([Vs1,Vs2,Esvs]),union([Us1,Us2,Esus]),St2}. pat_bit_size(default, St) -> {default,[],[],St}; pat_bit_size({atom,La,all}=All, St) -> {All,[],[],St}; pat_bit_size({var,Lv,V}=Var, St) -> {Var,[],[V],St}; pat_bit_size(Size, St) -> Line = element(2, Size), {value,Sz,Bs} = erl_eval:expr(Size, erl_eval:new_bindings()), {{integer,Line,Sz},[],[],St}. make_bit_type(Line, default, Type0) -> case erl_bits:set_bit_type(default, Type0) of {ok,all,Bt} -> {{atom,Line,all},erl_bits:as_list(Bt)}; {ok,Size,Bt} -> {{integer,Line,Size},erl_bits:as_list(Bt)} end; make_bit_type(Line, Size, Type0) -> %Integer or 'all' {ok,Size,Bt} = erl_bits:set_bit_type(Size, Type0), {Size,erl_bits:as_list(Bt)}. %% expr_bin([Element], [VisibleVar], State) -> %% {[Element],[NewVar],[UsedVar],State}. expr_bin(Es0, Vs, St) -> Es1 = bin_expand_strings(Es0), foldr(fun (E, Acc) -> bin_element(E, Vs, Acc) end, {[],[],[],St}, Es1). bin_element({bin_element,Line,Expr,Size,Type}, Vs, {Es,Esvs,Esus,St0}) -> {Expr1,Vs1,Us1,St1} = expr(Expr, Vs, St0), {Size1,Vs2,Us2,St2} = if Size == default -> {default,[],[],St1}; true -> expr(Size, Vs, St1) end, {Size2,Type1} = make_bit_type(Line, Size1, Type), {[{bin_element,Line,Expr1,Size2,Type1}|Es], union([Vs1,Vs2,Esvs]),union([Us1,Us2,Esus]),St2}. bin_expand_strings(Es0) -> foldr(fun ({bin_element,Line,{string,_,S},default,default}, Es) -> foldr(fun (C, Es) -> [{bin_element,Line,{integer,Line,C},default,default}|Es] end, Es, S); (E, Es) -> [E|Es] end, [], Es0). %% new_var_name(State) -> {VarName,State}. new_var_name(St) -> C = St#expand.vcount, {list_to_atom("%" ++ integer_to_list(C)),St#expand{vcount=C+1}}. %% new_var(Line, State) -> {Var,State}. new_var(L, St0) -> {New,St1} = new_var_name(St0), {{var,L,New},St1}. %% new_vars(Count, Line, State) -> {[Var],State}. %% Make Count new variables. new_vars(N, L, St) -> new_vars(N, L, St, []). new_vars(N, L, St0, Vs) when N > 0 -> {V,St1} = new_var(L, St0), new_vars(N-1, L, St1, [V|Vs]); new_vars(0, L, St, Vs) -> {Vs,St}. %% make_list(TermList, Line) -> ConsTerm. make_list(Ts, Line) -> foldr(fun (H, T) -> {cons,Line,H,T} end, {nil,Line}, Ts). string_to_conses(Line, Cs, Tail) -> foldr(fun (C, T) -> {cons,Line,{integer,Line,C},T} end, Tail, Cs). %% call_fault(Line, Reason) -> Expr. %% Build a call to erlang:fault/1 with reason Reason. call_fault(L, R) -> {call,L,{remote,L,{atom,L,erlang},{atom,L,fault}},[R]}. %% new_in(Before, RegionList) -> %% {NewInAll,NewInSome} %% Return the variables new in all clauses and those new in some clauses. new_in(Before, Region) -> InAll = intersection(Region), InSome = union(Region), NewInAll = subtract(InAll, Before), NewInSome = subtract(subtract(InSome, Before), NewInAll), {NewInAll,NewInSome}. %% For storing the import list we use our own version of the module dict. %% This is/was the same as the original but we must be sure of the format %% (sorted list of pairs) so we can do ordset operations on them (see the %% the function eof/2. We know an empty set is []. %% import(Line, Imports, State) -> %% State' %% imported(Name, Arity, State) -> %% {yes,Module} | no %% Handle import declarations and est for imported functions. No need to %% check when building imports as code is correct. import(Line, {Mod,Fs}, St) -> Mfs = list_to_set(Fs), St#expand{imports=add_imports(Mod, Mfs, St#expand.imports)}. add_imports(Mod, [F|Fs], Is) -> add_imports(Mod, Fs, store(F, Mod, Is)); add_imports(Mod, [], Is) -> Is. imported(F, A, St) -> case find({F,A}, St#expand.imports) of {ok,Mod} -> {yes,Mod}; error -> no end. %% This is our own version of the module dict which is/was the same as %% the original but we must be sure of the format (sorted list of pairs) %% so we can do ordset operations on them. We know an empty set is []. %% find(Key, Dictionary) -> {ok,Value} | error find(Key, [{K,Value}|D]) when Key > K -> find(Key, D); find(Key, [{K,Value}|_]) when Key == K -> {ok,Value}; find(Key, [{K,Value}|_]) when Key < K -> error; find(Key, []) -> error. %% store(Key, Value, Dictionary) -> Dictionary. store(Key, New, [{K,_}=Old|Dict]) when Key > K -> [Old|store(Key, New, Dict)]; store(Key, New, [{K,Old}|Dict]) when Key == K -> [{Key,New}|Dict]; store(Key, New, [{K,_}=Old|Dict]) when Key < K -> [{Key,New},Old|Dict]; store(Key, New, []) -> [{Key,New}]. From etxuwig@REDACTED Tue Oct 17 08:58:06 2000 From: etxuwig@REDACTED (Ulf Wiger) Date: Tue, 17 Oct 2000 08:58:06 +0200 (MET DST) Subject: Algorithmic lists In-Reply-To: Message-ID: On Mon, 16 Oct 2000, Andy with Recycled Electrons wrote: >Tell me if I'm wrong, but lazxy evaluation would screw up Real Time, >right? > >Andy >Recycled Electrons Well, not any more than would the otherwise mandatory rewrite into an ets:first()/ets:next() loop. The real-time behaviour will be the same, but the amount of changes needed to the code will be less with a lazy list syntax. In a system like the AXD 301, it is taken for granted that programmers must know what they are doing at all times (or at least most of the time). Another example of something that can screw up real time behaviuor is distributed message passing; it's a vital part of our system, but its use must be tightly controlled. Obviously, with lazy lists, it's quite easy to create an infinite loop, but not much more so than with the first program Erlang we teach: factorial(N) -> N * factorial(N-1); factorial(0) -> 1. (yes, I know this function can loop endlessly.) /Uffe >> > We (a few lunatics at AXD 301) would like to see the lists module >> > support an alternative formulation of lists. >> >> They are indeed elegant, and Joe was correct - they're called lazy lists, >> and should be implemented using zero-argument funs. To give a type >> declaration of sorts: >> >> LazyList :: [] | [term() | fun() -> LazyList] >> >> Writing a module 'lazy_lists' should be a doddle. In fact, I might do it >> myself. >> >> /Richard Carlsson >> >> >> Richard Carlsson (richardc@REDACTED) (This space intentionally left blank.) >> E-mail: Richard.Carlsson@REDACTED WWW: http://www.csd.uu.se/~richardc/ >> >> > >Sincerely, > >Andy Allen >Recycled Electrons >email: andy@REDACTED > > > -- Ulf Wiger tfn: +46 8 719 81 95 Strategic Product & System Management mob: +46 70 519 81 95 Ericsson Telecom AB, Datacom Networks and IP Services Varuv?gen 9, ?lvsj?, S-126 25 Stockholm, Sweden From etxuwig@REDACTED Tue Oct 17 09:10:58 2000 From: etxuwig@REDACTED (Ulf Wiger) Date: Tue, 17 Oct 2000 09:10:58 +0200 (MET DST) Subject: sys_pre_expand compiler warning (Re: Algorithmic lists) In-Reply-To: Message-ID: On Mon, 16 Oct 2000, Ulf Wiger wrote: > >Here's my first (and perhaps last) hack of sys_pre_expand.erl. >It handles at least my very simple test function with a list >comprehension that is fed a lazy list: BTW, when I compiled sys_pre_expand.erl, I got the following compiler warning: > erlc -W sys_pre_expand.erl /home/etxuwig/work/erlang/lazy/sys_pre_expand.erl:703: Warning: variable 'Es' shadowed in 'fun' The function in question looks like this: bin_expand_strings(Es0) -> foldr(fun ({bin_element,Line,{string,_,S},default,default}, Es) -> foldr(fun (C, Es) -> [{bin_element,Line,{integer,Line,C},default,default}|Es] end, Es, S); (E, Es) -> [E|Es] end, [], Es0). Now, it doesn't really seem like a bug, but wouldn't it be awfully easy to avoid the warning and resulting confusion? ;) /Uffe -- Ulf Wiger tfn: +46 8 719 81 95 Strategic Product & System Management mob: +46 70 519 81 95 Ericsson Telecom AB, Datacom Networks and IP Services Varuv?gen 9, ?lvsj?, S-126 25 Stockholm, Sweden From rv@REDACTED Tue Oct 17 11:03:46 2000 From: rv@REDACTED (Robert Virding) Date: Tue, 17 Oct 2000 11:03:46 +0200 Subject: sys_pre_expand compiler warning (Re: Algorithmic lists) In-Reply-To: Your message of "Tue, 17 Oct 2000 09:10:58 +0200." Message-ID: <200010170903.LAA15107@trana.bluetail.com> Ulf Wiger writes: >On Mon, 16 Oct 2000, Ulf Wiger wrote: > >> >>Here's my first (and perhaps last) hack of sys_pre_expand.erl. >>It handles at least my very simple test function with a list >>comprehension that is fed a lazy list: > >BTW, when I compiled sys_pre_expand.erl, I got the following compiler >warning: > >> erlc -W sys_pre_expand.erl >/home/etxuwig/work/erlang/lazy/sys_pre_expand.erl:703: Warning: >variable 'Es' shadowed in 'fun' > >The function in question looks like this: > >bin_expand_strings(Es0) -> > foldr(fun ({bin_element,Line,{string,_,S},default,default}, Es) -> > foldr(fun (C, Es) -> > >[{bin_element,Line,{integer,Line,C},default,default}|Es] > end, Es, S); > (E, Es) -> [E|Es] > end, [], Es0). > > >Now, it doesn't really seem like a bug, but wouldn't it be awfully >easy to avoid the warning and resulting confusion? ;) The reason that there is a warning here (and in funs when head variables shadow) is that this is a departure from standard Erlang practice of NOT shadowing. For example in X = ..., case ... of {a,X} -> the X in the pattern inside the case does not shadow the outer X. (Which is contrary to most other functional languages) It was felt that it would be better to highlight this to avoid confusion. Otherwise people might expect that a head variable in a fun should match if it already bound. I agree it is sometimes irretating when I KNOW what I am doing (which of course I always do :-), but this is in keeping with the philosophy o ftrying to keep things simple, consistent and easy to learn so we highlight inconsistencies. Robert -- Robert Virding Tel: +46 (0)8 545 55 017 Alteon Web Systems Email: rv@REDACTED S:t Eriksgatan 44 WWW: http://www.bluetail.com/~rv SE-112 34 Stockholm, SWEDEN "Folk s?ger att jag inte bryr mig om n?gonting, men det skiter jag i". From richardc@REDACTED Tue Oct 17 11:53:50 2000 From: richardc@REDACTED (Richard Carlsson) Date: Tue, 17 Oct 2000 11:53:50 +0200 (MET DST) Subject: Algorithmic lists In-Reply-To: Message-ID: On Mon, 16 Oct 2000, Andy with Recycled Electrons wrote: > Let me clarify... > > Accessing a list is a simple, real-time operation. > > Accessing a Lazy List may mean accessing a megapixel image from Hong Kong, > where it is ray traced in true color based on several dozen light > sources. > > In other words, there would be no gurantee of ANY timing constraints. As > long as the "real time" system realizes this, everyone is happy. Programming with lazy evaluation as the general rule (as in Haskell) does indeed make it difficult for the programmer (unless he happens to be John Hughes) to get an intuitive grasp of how time-consuming a particular computation will be. It does not have to involve disk or network access to be so: one can fairly easily happen to create a function that seems straightforward but actually has exponential space complexity, and if you are not one of the cognoscenti, the ways of rewriting it so it behaves nicer may simply appear like waving so many dead chickens. That said, one can do a lot of neat things with lazy evaluation (even if you simulate it in a strict language like Erlang), and a library for working with lazy lists could be a nice thing to have, since it seems more likely that someone who uses that library does more or less know what he is doing, and why. > Also, is there a way to overload operators (not function calls) in > Erlang? I'd love to overload "+" or "-" for a specific kind of input, > so that a call is made to my function. The data units I am passing in > are 3-element tuples right now, but I can change that. No, there isn't. (Or rather, yes, there is, it's called "parse transforms", and you should really not be doing it.) My advice: implement a nice abstract datatype with functions "add(X, Y)", "subtract(X, Y)", etc., or if you really feel you want `+' and `-', you can actually call your functions "'+'(X, Y)" and "'-'(X, Y)" as long as you use single-quoting. /Richard Carlsson Richard Carlsson (richardc@REDACTED) (This space intentionally left blank.) E-mail: Richard.Carlsson@REDACTED WWW: http://www.csd.uu.se/~richardc/ From rv@REDACTED Tue Oct 17 12:02:41 2000 From: rv@REDACTED (Robert Virding) Date: Tue, 17 Oct 2000 12:02:41 +0200 Subject: Algorithmic lists In-Reply-To: Your message of "Mon, 16 Oct 2000 16:18:56 CDT." Message-ID: <200010171002.MAA15300@trana.bluetail.com> Andy with Recycled Electrons writes: >Let me clarify... > >Accessing a list is a simple, real-time operation. > >Accessing a Lazy List may mean accessing a megapixel image from Hong Kong, >where it is ray traced in true color based on several dozen light >sources. > >In other words, there would be no gurantee of ANY timing constraints. As >long as the "real time" system realizes this, everyone is happy. This whole argument of whether lazy lists violates the real time constraints is definitely weird! Firstly, accessing a whole list is definitely not a simple operation, only accessing the first cons cell. For all the rest you step down the list. Secondly, the real difference between "normal" lists and "lazy" lists is WHEN you build them. For a "normal" list you build the whole list in one go at creation time. For "lazy" lists you build the list one cons cell at a time when you access the list. In essence you amortize the list creation work over reading. Thirdly, this does not affect the real time constraints of ERLANG as you are not doing any new of type of action. It's still just evaluating functions, all you are doing is changing the time when you evaluate the functions. Fourthly, this may of course have a profound affect on the real time constraints of the APPLICATION. Sending (as an argument or message) a lazy list can affect when, where and who actually builds the list. Fifthly, seeing Erlang doesn't natively support lazy evaluation you always have to KNOW whether the tail is a "normal" tail or an unevaluated "lazy" tail. also every time you access the list you will have to re-evaluate the tails as Erlang will not automatically replace the tails with thier value. This severely limits it usefulness. Sixthly, in many lazy languages lazy lists are used as a form of communication bewtween coroutines. Erlang has processes for this. What is left is then infinite lists. Because of all this I don't really see that much use of lazy lists apart form as a fun programming exercise. Find me a real application and try to convert me. Personally I think fifthly is the real clincher. Robert P.S. I once toyed with idea of doing a real lazy Erlang. If you made message sending force the full evaluation of the message, and maybe force the evaluation of calls in which the result is ignored it would probably work. -- Robert Virding Tel: +46 (0)8 545 55 017 Alteon Web Systems Email: rv@REDACTED S:t Eriksgatan 44 WWW: http://www.bluetail.com/~rv SE-112 34 Stockholm, SWEDEN "Folk s?ger att jag inte bryr mig om n?gonting, men det skiter jag i". From rv@REDACTED Tue Oct 17 12:11:33 2000 From: rv@REDACTED (Robert Virding) Date: Tue, 17 Oct 2000 12:11:33 +0200 Subject: Overloading (was Re: Algorithmic lists ) In-Reply-To: Your message of "Mon, 16 Oct 2000 16:18:56 CDT." Message-ID: <200010171011.MAA15337@trana.bluetail.com> Andy with Recycled Electrons writes: > >Also, is there a way to overload operators (not function calls) in Erlang? >I'd love to overload "+" or "-" for a specific kind of input, so that a >call is made to my function. The data units I am passing in are 3-element >tuples right now, but I can change that. > >I.E., In the second half of this eMail I am searching for a way to define >my own data type in Erlang and overload existing operators based on it. >Does anyone have an example? No, we didn't really think it is such a good idea. It is VERY NICE to be able to look at an operator and KNOW what the argument and result types must be. Anyway with a dynamically typed language it is not so easy as you have to redefine the functions/built-ins/primitives the operators end up calling. You can't fix that at compile time. You could perhaps do it with parse transforms, but again without proper type information it is not easy. The "proper" way to define a set of functions operating on the types. Robert -- Robert Virding Tel: +46 (0)8 545 55 017 Alteon Web Systems Email: rv@REDACTED S:t Eriksgatan 44 WWW: http://www.bluetail.com/~rv SE-112 34 Stockholm, SWEDEN "Folk s?ger att jag inte bryr mig om n?gonting, men det skiter jag i". From vladdu@REDACTED Tue Oct 17 12:31:27 2000 From: vladdu@REDACTED (Vlad Dumitrescu) Date: Tue, 17 Oct 2000 12:31:27 +0200 Subject: Overloading (was Re: Algorithmic lists ) References: <200010171011.MAA15337@trana.bluetail.com> Message-ID: >No, we didn't really think it is such a good idea. It is VERY NICE to >be able to look at an operator and KNOW what the argument and result >types must be. One alternative solution (which I don't really like much either) would be to allow definition of infix functions, and use for example "X @+ Y" instead of "add(X, Y)" Vlad From rv@REDACTED Tue Oct 17 12:51:26 2000 From: rv@REDACTED (Robert Virding) Date: Tue, 17 Oct 2000 12:51:26 +0200 Subject: Overloading (was Re: Algorithmic lists ) In-Reply-To: Your message of "Tue, 17 Oct 2000 12:31:27 +0200." Message-ID: <200010171051.MAA15530@trana.bluetail.com> "Vlad Dumitrescu" writes: >>No, we didn't really think it is such a good idea. It is VERY NICE to >>be able to look at an operator and KNOW what the argument and result >>types must be. > >One alternative solution (which I don't really like much either) would be to >allow definition of infix functions, and use for example "X @+ Y" instead >of "add(X, Y)" There are a couple of problems with this: 1. To make it general you would have to make a transformation like "X @+ Y" --> '+'(X, Y) otherwise it would be a pain to define, and remember, the operator/name translation. 2. Today the compiler makes the transformation "X + Y" --> erlang:'+'(X, Y) for operators. It just hasn't been standardised. 3. If the transformations go to local functions it really it that useful. 4. There is a proposal for standard Erlang (Proposal 16 "Local function names") which together with some suitably defined rewrite rules for operators, or just crafty synonyms would probably do the trick. 5. I don't LIKE overloaded operators! 6. And, as I said before, without static typing it not really that useful as it is difficult to control. In Erlang you overload function names but you qualify them with modules when they ork on different things. Perhaps "X @foo:+ Y"? :-) :-) 7. As the great Cato once said "I still think that operator overloading should be burned!" Robert -- Robert Virding Tel: +46 (0)8 545 55 017 Alteon Web Systems Email: rv@REDACTED S:t Eriksgatan 44 WWW: http://www.bluetail.com/~rv SE-112 34 Stockholm, SWEDEN "Folk s?ger att jag inte bryr mig om n?gonting, men det skiter jag i". From etxuwig@REDACTED Tue Oct 17 12:52:18 2000 From: etxuwig@REDACTED (Ulf Wiger) Date: Tue, 17 Oct 2000 12:52:18 +0200 (MET DST) Subject: Algorithmic lists In-Reply-To: Message-ID: On Mon, 16 Oct 2000, Ulf Wiger wrote: > >Here's my first (and perhaps last) hack of sys_pre_expand.erl. >It handles at least my very simple test function with a list >comprehension that is fed a lazy list: OK, since noone complained too much, here's a hack of lists.erl, where most functions have been enhanced to operate on both eager and lazy lists. The functions that do not accept lazy lists are: - append() and subtract(), of course, since they rely on BIFs (append() works for some combinations, see below) - suffix(), since it was difficult to modify slightly without losing efficiency - sort(), merge(), rmerge() - keymember() and keysearch(), since they are BIFs - keysort(), keymerge() I wrote a testsuite called listtest.erl. I've reformatted the output slightly to make it more readable. It would be interesting if someone with ready access to estone could see if the modified functions are now slower for eager lists. BTW, with these modifications, list comprehensions in the Erlang shell also work with lazy lists. /Uffe PS I just discovered the ??Arg macro syntax. Great for stuff like a simple test module. (: 1> listtest:run(concise). "lists:seq(1, 5)" -> [1,2,3,4,5] "lists:seq(1, 3)" -> [1,2,3] "seq(1, 5)" -> [1|#Fun] "seq(1, 2)" -> [1|#Fun] "lists:append(Seq15, Seq15)" -> [1,2,3,4,5,1,2,3,4,5] "lists:append(Seq15, Lzy15)" -> [1,2,3,4,5,1|#Fun] "lists:append(Lzy15, Lzy15)" ->{'EXIT',badarg} "lists:append([Seq15, Seq15])" -> [1,2,3,4,5,1,2,3,4,5] "lists:append([Seq15, Lzy15])" -> [1,2,3,4,5,1|#Fun] "lists:append([Lzy15, Seq15])" ->{'EXIT',badarg} "lists:reverse(Seq15)" -> [5,4,3,2,1] "lists:reverse(Lzy15)" ->{'EXIT',badarg} "lists:nth(3, Seq15)" -> 3 "lists:nth(3, Lzy15)" -> 3 "lists:nthtail(3, Seq15)" -> [4,5] "lists:nthtail(3, Lzy15)" -> [4|#Fun] "lists:prefix(Seq13, Seq15)" -> true "lists:prefix(Seq13, Lzy15)" -> true "lists:prefix(Lzy13, Seq15)" -> true "lists:prefix(Lzy15, Seq13)" -> false "lists:prefix(Seq15, Seq13)" -> false "lists:prefix(Seq15, Lzy13)" -> false "lists:last(Seq15)" -> 5 "lists:last(Lzy15)" -> 5 "lists:sum(Seq15)" -> 15 "lists:sum(Lzy15)" -> 15 "lists:min(Seq15)" -> 1 "lists:min(Lzy15)" -> 1 "lists:max(Seq15)" -> 5 "lists:max(Lzy15)" -> 5 "lists:sublist(Seq15, 2, 2)" -> [2,3] "lists:sublist(Lzy15, 2, 2)" -> [2,3] "lists:delete(3, Seq15)" -> [1,2,4,5] "lists:delete(3, Lzy15)" -> [1,2,4|#Fun] "lists:flatten([Seq15, [Seq15]])" -> [1,2,3,4,5,1,2,3,4,5] "lists:flatten([Seq15, [Lzy15]])" -> [1,2,3,4,5,1,2,3,4,5] "lists:flatten([Lzy15, [Lzy15]])" -> [1,2,3,4,5,1,2,3,4,5] "lists:flatten([Lzy15, [Seq15]])" -> [1,2,3,4,5,1,2,3,4,5] "lists:flatlength([Seq15, [Seq15]])" -> 10 "lists:flatlength([Seq15, [Lzy15]])" -> 10 "lists:flatlength([Lzy15, [Lzy15]])" -> 10 "lists:flatlength([Lzy15, [Seq15]])" -> 10 "lists:keydelete(3, 1, [{N, a} || N <- seq(1, 5)])" -> [{1,a},{2,a},{4,a},{5,a}] "lists:keydelete(3, 1, [{N, a} || N <- lists:seq(1, 5)])" -> [{1,a},{2,a},{4,a},{5,a}] "lists:keyreplace(3, 1, [{N, a} || N <- seq(1, 5)], 33)" -> [{1,a},{2,a},33,{4,a},{5,a}] "lists:keyreplace(3, 1, [{N, a} || N <- lists:seq(1, 5)], 33)" -> [{1,a},{2,a},33,{4,a},{5,a}] "lists:keymap(fun(X) -> [X] end, 2, [{N, a} || N <- seq( 1, 5)])" -> lists:seq(1, 5)])" -> [{1,[a]},{2,[a]},{3,[a]},{4,[a]},{5,[a]}] "lists:keymap(fun(X) -> [X] end, 2, [{N, a} || N <- lists:seq(1, 5)])" -> [{1,[a]},{2,[a]},{3,[a]},{4,[a]},{5,[a]}] "lists:all(fun(N) when integer(N) -> true ;(_) -> false end, Seq15)" -> true "lists:all(fun(N) when integer(N) -> true ;(_) -> false end, Lzy15)" -> true "lists:all(fun(N) when N > 1, N < 4 -> true ;(_) -> false end, Seq15)" -> false "lists:all(fun(N) when N > 1, N < 4 -> true ;(_) -> false end, Lzy15)" -> false "lists:any(fun(N) when N > 1, N < 4 -> true ;(_) -> false end, Seq15)" -> true "lists:any(fun(N) when N > 1, N < 4 -> true ;(_) -> false end, Lzy15)" -> true "lists:map(fun(N) -> N + 1 end, Seq15)" -> [2,3,4,5,6] "lists:map(fun(N) -> N + 1 end, Lzy15)" -> [2,3,4,5,6] "lists:flatmap(fun(N) -> [N + 1] end, Seq15)" -> [2,3,4,5,6] "lists:flatmap(fun(N) -> [N + 1] end, Lzy15)" -> [2,3,4,5,6] "lists:foldl(fun(N, Acc) -> Acc + 1 end, 0, Seq15)" -> 5 "lists:foldl(fun(N, Acc) -> Acc + 1 end, 0, Lzy15)" -> 5 "lists:zf(fun(N) when N>1,N<4 ->{true,[N]} ;(_)-> false end, Seq15)" -> [[2],[3]] "lists:zf(fun(N) when N>1,N<4 ->{true, [N]} ;(_)-> false end, Lzy15)" -> [[2],[3]] i:1 i:2 i:3 i:4 i:5 "lists:foreach(fun(N) -> io : format(\"i:~p~n\", [N]) end, Seq15)" -> ok i:1 i:2 i:3 i:4 i:5 i:1 i:2 i:3 i:4 i:5 "lists:foreach(fun(N) -> io:format(\"i:~p~n\",[N]) end, Lzy15)" -> ok i:1 i:2 i:3 i:4 i:5 "lists:mapfoldl(fun(N, Acc) ->{N + 1, Acc + N} end, 0,Seq15)" -> {[2,3,4,5,6],15} "lists:mapfoldl(fun(N, Acc) ->{N + 1, Acc + N} end, 0,Lzy15)" -> {[2,3,4,5,6],15} "lists:mapfoldr(fun(N, Acc) ->{N + 1, Acc + N} end, 0,Seq15)" -> {[2,3,4,5,6],15} "lists:mapfoldr(fun(N, Acc) ->{N + 1, Acc + N} end, 0,Lzy15)" -> {[2,3,4,5,6],15} "lists:takewhile(fun(N) when N < 3 -> true ;(_) -> false end, Seq15)" -> [1,2] "lists:takewhile(fun(N) when N < 3 -> true ;(_) -> false end, Lzy15)" -> [1,2] "lists:dropwhile(fun(N) when N < 3 -> true ;(_) -> false end, Seq15)" -> [3,4,5] "lists:dropwhile(fun(N) when N < 3 -> true ;(_) -> false end, Lzy15)" -> [3|#Fun] "lists:splitwith(fun(N) when N < 3 -> true ;(_) -> false end, Seq15)" -> {[1,2],[3,4,5]} "lists:splitwith(fun(N) when N < 3 -> true ;(_) -> false end, Lzy15)" -> {[1,2],[3|#Fun]} -- Ulf Wiger tfn: +46 8 719 81 95 Strategic Product & System Management mob: +46 70 519 81 95 Ericsson Telecom AB, Datacom Networks and IP Services Varuv?gen 9, ?lvsj?, S-126 25 Stockholm, Sweden -------------- next part -------------- A non-text attachment was scrubbed... Name: lazy.tgz Type: application/octet-stream Size: 14014 bytes Desc: lazy.tgz URL: From rv@REDACTED Tue Oct 17 12:53:33 2000 From: rv@REDACTED (Robert Virding) Date: Tue, 17 Oct 2000 12:53:33 +0200 Subject: Overloading (was Re: Algorithmic lists ) In-Reply-To: Your message of "Tue, 17 Oct 2000 12:51:26 +0200." <200010171051.MAA15530@trana.bluetail.com> Message-ID: <200010171053.MAA15564@trana.bluetail.com> Robert Virding writes: > >4. There is a proposal for standard Erlang >(Proposal 16 "Local function names") which together with some suitably >defined rewrite rules for operators, or just crafty synonyms would >probably do the trick. Forgot to say that you can find a link to the Erlang spec pages in my home page. -- Robert Virding Tel: +46 (0)8 545 55 017 Alteon Web Systems Email: rv@REDACTED S:t Eriksgatan 44 WWW: http://www.bluetail.com/~rv SE-112 34 Stockholm, SWEDEN "Folk s?ger att jag inte bryr mig om n?gonting, men det skiter jag i". From etxuwig@REDACTED Tue Oct 17 13:12:24 2000 From: etxuwig@REDACTED (Ulf Wiger) Date: Tue, 17 Oct 2000 13:12:24 +0200 (MET DST) Subject: Algorithmic lists In-Reply-To: <200010171002.MAA15300@trana.bluetail.com> Message-ID: On Tue, 17 Oct 2000, Robert Virding wrote: rv>Because of all this I don't really see that much use of lazy rv>lists apart form as a fun programming exercise. Find me a real rv>application and try to convert me. Personally I think fifthly rv>is the real clincher. Not to say that it wasn't fun, but we started this as an approach to be able to change data access patterns in existing code without changing too much code in the process. We see quite often that people solve their problems by handling large lists of data. If the data is stored in mnesia or ets, they call ets:tab2list/1 and then traverse the data as a list. This is a very bad idea if the tables are large, since Erlang will allocate about three times as much memory as is needed to actually represent the data on the process heap. Our approach, when we come across these patterns, is to rewrite the iteration so that they step through a table instead of building a list and then stepping through it. But with thousands of lines of list-processing code, we'd have to rewrite a ton of function clauses if we depart from the list syntax. Often, we end up doing an almost complete rewrite for this reason (*). We feel that this is a simple extension that gives us much more flexibility, and allows us to experiment with physical data representations by changing only a few lines of code -- and without changing the fundamental nature of what we're doing, which is iterating over a set of data objects (**). /Uffe (*) Of course, these rewrites are usually warranted for other reasons, but timing is always an issue in production projects. If we can, we'd like to schedule complete rewrites to some other time than during the system test phase -- which is a fairly common place to find these problems. (**) Also, this is not so easy to address by simply trying to educate programmers about how to write code, so that we can easily change it; people tend to use the most intuitive constructs, and we should encourage this as much as possible. Operating on a list is one of the most intuitive concepts, so it is used very often. -- Ulf Wiger tfn: +46 8 719 81 95 Strategic Product & System Management mob: +46 70 519 81 95 Ericsson Telecom AB, Datacom Networks and IP Services Varuv?gen 9, ?lvsj?, S-126 25 Stockholm, Sweden From richardc@REDACTED Tue Oct 17 13:15:15 2000 From: richardc@REDACTED (Richard Carlsson) Date: Tue, 17 Oct 2000 13:15:15 +0200 (MET DST) Subject: Algorithmic lists In-Reply-To: Message-ID: On Tue, 17 Oct 2000, Ulf Wiger wrote: > OK, since noone complained too much, here's a hack of lists.erl, > where most functions have been enhanced to operate on both eager > and lazy lists. Well, your hacked functions give correct output, but do they behave as you intented? They are all eager, in that if you apply your new `map' on a lazy list, it forces evaluation of the whole list. A more generally useful implementation would return a new lazy list, where the `map' operation is not performed on any element until that element is requested. Then you can create a list like the following: lazy_lists:map(fun (X) -> X * 2 end, lazy_lists:natural_numbers()) (the list of all even nonnegative numbers) and then pass the result to another function, which can pick as many elements from the list as it wants, or none at all. /Richard Carlsson Richard Carlsson (richardc@REDACTED) (This space intentionally left blank.) E-mail: Richard.Carlsson@REDACTED WWW: http://www.csd.uu.se/~richardc/ From etxuwig@REDACTED Tue Oct 17 13:29:19 2000 From: etxuwig@REDACTED (Ulf Wiger) Date: Tue, 17 Oct 2000 13:29:19 +0200 (MET DST) Subject: Algorithmic lists In-Reply-To: Message-ID: On Tue, 17 Oct 2000, Richard Carlsson wrote: > >On Tue, 17 Oct 2000, Ulf Wiger wrote: > >> OK, since noone complained too much, here's a hack of lists.erl, >> where most functions have been enhanced to operate on both eager >> and lazy lists. > >Well, your hacked functions give correct output, but do they behave >as you intented? They are all eager, in that if you apply your new >`map' on a lazy list, it forces evaluation of the whole list. Yes, this is exactly was was intended for the lists.erl module, since it has to be backwards compatible. Functions that generate lazy lists would have to go into your lazy_lists module (is it finished yet). ;) /Uffe >A more generally useful implementation would return a new lazy list, >where the `map' operation is not performed on any element until that >element is requested. Then you can create a list like the following: > > lazy_lists:map(fun (X) -> X * 2 end, > lazy_lists:natural_numbers()) > >(the list of all even nonnegative numbers) and then pass the result >to another function, which can pick as many elements from the list >as it wants, or none at all. > > /Richard Carlsson -- Ulf Wiger tfn: +46 8 719 81 95 Strategic Product & System Management mob: +46 70 519 81 95 Ericsson Telecom AB, Datacom Networks and IP Services Varuv?gen 9, ?lvsj?, S-126 25 Stockholm, Sweden From thomasl@REDACTED Tue Oct 17 13:22:56 2000 From: thomasl@REDACTED (Thomas Lindgren) Date: Tue, 17 Oct 2000 13:22:56 +0200 Subject: Overloading (was Re: Algorithmic lists ) In-Reply-To: <200010171011.MAA15337@trana.bluetail.com> (message from Robert Virding on Tue, 17 Oct 2000 12:11:33 +0200) Message-ID: <200010171122.NAA01089@lammgam.bluetail.com> > Anyway with a dynamically typed language it is not so easy as you have > to redefine the functions/built-ins/primitives the operators end up > calling. You can't fix that at compile time. Smalltalk and Scheme have traditionally permitted redefinition of builtins. To get reasonable performance, it seems one has to do dynamic compilation (Smalltalk) or declare that you want the standard primitives (Scheme). Thomas -- Thomas Lindgren thomasl+junk@REDACTED Alteon Websystems Sweden http://www.bluetail.com From klacke@REDACTED Tue Oct 17 15:16:42 2000 From: klacke@REDACTED (Klacke) Date: Tue, 17 Oct 2000 15:16:42 +0200 Subject: Windows build In-Reply-To: <39EAB965.27FC33EC@cellpt.com> References: <39EAB965.27FC33EC@cellpt.com> Message-ID: <20001017151642.A25947@bluetail.com> On Mon, Oct 16, 2000 at 10:16:37AM +0200, Per Bergqvist wrote: > There are some other more important problems as well such as the use of > MSVCRT setargv and the > way quoting of parameters is done which inhibits use of the make files > as is. > I have a build environment more or less ready for Windows but need > somebody to volunteer to assist. > I hoped to finish it a couple of weeks ago but so far I haven't turned > on my PC at home the last three weeks.... > If I knew that somebody really would like to test it I'll probably kick > myself somewhere and fix it. This spring I used Per's build environment to build otp_r6b for win32. It's obviously much better to build on the win32 machine directly than to build (and debug) in a mixed adhoc environment. Use Cygnus gmake, openssl, perl MSVC++ nmake ... We aimed to ship our stuff on win32 as well as unices. (Un ...)fortunately this was cancelled so we never shiped. However here's the build_nt directory which I used. It's supposed to reside just under $ERL_TOP /klacke -- Claes Wikstrom Bluetail AB http://www.bluetail.com -------------- next part -------------- A non-text attachment was scrubbed... Name: bnt.tgz Type: application/octet-stream Size: 4086 bytes Desc: not available URL: From vladdu@REDACTED Tue Oct 17 16:36:19 2000 From: vladdu@REDACTED (Vlad Dumitrescu) Date: Tue, 17 Oct 2000 16:36:19 +0200 Subject: Windows build References: <39EAB965.27FC33EC@cellpt.com> <20001017151642.A25947@bluetail.com> Message-ID: > We aimed to ship our stuff on win32 as well > as unices. (Un ...)fortunately this was cancelled so we never > shiped. However here's the build_nt directory which I used. > It's supposed to reside just under $ERL_TOP I will try it, thank you very much. A side note: It's a little confusing with all of you people at Bluetail that keep referring to Ericsson as "we"! :-) Vlad From richardc@REDACTED Tue Oct 17 17:55:30 2000 From: richardc@REDACTED (Richard Carlsson) Date: Tue, 17 Oct 2000 17:55:30 +0200 (MET DST) Subject: Lazy lists - an implementation. In-Reply-To: Message-ID: On Tue, 17 Oct 2000, Ulf Wiger wrote: > OK, since noone complained too much, here's a hack of lists.erl, > where most functions have been enhanced to operate on both eager > and lazy lists. And here is my first version of a lazy lists library. Seems to work ok. Nothing is evaluated unless necessary. Note, though, that my interpretation of a lazy list is (like in the example that Tobbe gave, due to Phil Wadler) a fun: LazyList :: () -> [] | [term() | LazyList] This makes things easier. The way Ulf implemented them can be seen as using an explicit push-back buffer for an initial number of known elements, but this makes programming with fully lazy lists more difficult, because many more cases must be handled. (In my implementation, you can push stuff back using e.g. `cons(H, T)' when necessary.) Also, in Ulfs current implementation, either a list is empty or the first element has been computed. (This could be fixed, though.) /Richard Richard Carlsson (richardc@REDACTED) (This space intentionally left blank.) E-mail: Richard.Carlsson@REDACTED WWW: http://www.csd.uu.se/~richardc/ -------------- next part -------------- %% =============================================================== %% Lazy lists %% %% Copyright (C) 2000 Richard Carlsson %% %% This library is free software; you can redistribute it and/or %% modify it under the terms of the GNU Lesser General Public %% License as published by the Free Software Foundation; either %% version 2 of the License, or (at your option) any later %% version. %% %% This library is distributed in the hope that it will be useful, %% but WITHOUT ANY WARRANTY; without even the implied warranty of %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the %% GNU Lesser General Public License for more details. %% %% You should have received a copy of the GNU Lesser General %% Public License along with this library; if not, write to the %% Free Software Foundation, Inc., 59 Temple Place, Suite 330, %% Boston, MA 02111-1307 USA %% %% Author contact: richardc@REDACTED %% =============================================================== -module(lazy_lists). -vsn('$Id: lazy_lists.erl,v 1.1 2000/10/17 15:26:28 richardc Exp $'). -author('richardc@REDACTED'). -copyright('Copyright (C) 2000 Richard Carlsson'). -export([append/2, cons/2, constant/1, duplicate/2, eval/1, filter/2, first/2, foldl/3, foldr/3, foreach/2, integers/1, lazy/1, map/2, member/2, merge/3, nil/0, nth/2, nthtail/2, random/0, random_integers/1, seq/2, seq/3, sublist/3]). %% The type of the lists defined below is: %% %% LazyList :: () -> [] | [term() | LazyList] %% %% A lazy list is thus always a fun. One advantage is that if a %% producer has side effects (e.g., if it reads elements from a %% file), we need never trigger the next effect unless we really %% want to see the next element. Also, programming becomes very %% straightforward. Let's hope for better compilers to make this %% sort of programming more efficient. %% =============================================================== %% The following functions compute new lazy lists without forcing %% evaluation of their input. %% The empty list nil() -> fun () -> [] end. %% The list consisting of H followed by all elements of L. cons(H, T) -> fun () -> [H | T] end. %% Completely evaluates a lazy list. Make sure never to pass an %% infinite list to this function! eval(L) -> case L() of [H | T] -> [H | eval(T)]; [] -> [] end. %% Turns a proper normal list into a lazy list. lazy(L) -> fun () -> case L of [H | T] -> [H | lazy(T)]; [] -> [] end end. %% The list of all elements of L1 followed by all elements of L2. append(L1, L2) -> fun () -> case L1() of [] -> L2(); [H | T] -> [H | append(T, L2)] end end. %% The list of integers in the interval [From, To], in ascending %% order. seq(From, To) -> seq(From, To, 1). %% The list of integers [From, From + D, From + 2*D, ..., From + %% D*((To - From) mod D)]. The interval is empty if D does not %% have the same sign as the difference To - From. seq(From, To, D) when integer(From), integer(To), From < To, D > 0 -> fun () -> [From | seq(From + D, To, D)] end; seq(From, To, D) when integer(From), integer(To), To < From, D < 0 -> fun () -> [From | seq(From + D, To, D)] end; seq(From, To, D) when integer(From), integer(To), To == From -> fun () -> [From | nil()] end; seq(From, To, D) -> nil(). %% The list of integers starting at N. integers(N) -> fun () -> [N | integers(N + 1)] end. %% The infinite list of elements X. constant(X) -> fun () -> [X | constant(X)] end. %% The list of length N, where each element is X. duplicate(N, X) when integer(N) -> fun () -> if N > 0 -> [X | duplicate(N - 1, X)]; N == 0 -> [] end end. %% An infinite list of random floating-pint numbers in the %% interval [0.0, 1.0]. `random:seed' must be used (by the same %% process) to set the seed for the random number generator before %% this function is called. random() -> fun () -> [random:uniform() | random()] end. %% An infinite list of random integers in the interval [0, Max]. %% See `random' above for details. random_integers(Max) when Max >= 1 -> fun () -> [random:uniform(Max) | random_integers(Max)] end. %% The list consisting of the first N elements of L, or the list L %% if the length of L is not greater than N. first(0, L) -> nil(); first(N, L) when integer(N), N > 0 -> fun () -> case L() of [H | T] -> [H | first(N-1, T)]; [] -> [] end end. %% The list [EN, EN+1, ..., EN+D], if L is [E1, E2, ...]. sublist(N, D, L) when N > 0, D >= 0 -> if N > 1 -> fun () -> case L() of [H | T] -> (sublist(N - 1, D, T))(); [] -> [] end end; true -> first(D, L) end. %% The list [F(E1), F(E2), F(E3), ...] if L is [E1, E2, E3, ...]. map(F, L) -> fun () -> case L() of [H | T] -> [F(H) | map(F, T)]; [] -> [] end end. %% The list of all elements E in L (in the same order) for which %% P(E) returns `true'. P must return either `true' or `false' for %% all elements in L. filter(P, L) -> fun () -> case L() of [H | T] -> case P(H) of true -> [H | filter(P, T)]; false -> (filter(P, T))() end; [] -> [] end end. %% Returns the list of elements in L1 and L2 where the respective %% relative order of elements is preserved, and each element Y of %% L2 is ordered before the first possible X of L1 such that P(X, %% Y) yields `false'. P(X, Y) must yield either `true' or `false' %% for all X in L1 and Y in L2. (P can be read as "less than".) merge(P, L1, L2) -> fun () -> case L1() of [H1 | T1] -> case L2() of [H2 | T2] -> case P(H1, H2) of true -> [H1 | merge(P, T1, cons(H2, T2))]; false -> [H2 | merge(P, cons(H1, T1), T2)] end; [] -> [H1 | T1] end; [] -> L2() end end. %% =============================================================== %% All functions below may evaluate all or part of their input. %% Returns `true' if X is in the list L, and `false' otherwise. member(X, L) -> case L() of [H | T] -> if H == X -> true; true -> member(X, T) end; [] -> false end. %% Returns the Nth element of list L. nth(N, L) -> L1 = L(), if N > 1 -> nth(N - 1, tl(L1)); true -> hd(L1) end. %% Returns the list [AN+1, AN+2, ...] if L is [A1, A2, ..., AN, %% AN+1, AN+2, ...]. nthtail(N, L) when N > 1 -> nthtail(N - 1, tl(L())); nthtail(0, L) -> L. %% Computes (...((A F E1) F E2)... F EN), if L is [E1, E2, ..., %% EN] and F is a binary function (here written as an infix %% operator). foldl(A, F, L) -> case L() of [H | T] -> foldl(F(A, H), F, T); [] -> A end. %% Computes (E1 F ...(EN-1 F (EN F A))...), if L is [E1, E2, ..., %% EN] and F is a binary function (here written as an infix %% operator). foldr(L, F, A) -> case L() of [H | T] -> F(H, foldr(T, F, A)); [] -> A end. %% Evaluates F(E1), F(E2), ..., if L is [E1, E2, ...]. Always %% returns `ok'. foreach(F, L) -> case L() of [H | T] -> F(H), foreach(F, T); [] -> ok end. %% =============================================================== From bjarne@REDACTED Tue Oct 17 19:55:26 2000 From: bjarne@REDACTED (Bjarne =?iso-8859-1?Q?D=E4cker?=) Date: Tue, 17 Oct 2000 19:55:26 +0200 Subject: Photographs from EUC'2000 Message-ID: <39EC928E.1A7BDCF9@erix.ericsson.se> Hello Please see http://www.erlang.se/euc/00/pics.html Nice memories for those who were there and an impression for those who weren't. Best regards Bjarne From Chandrashekhar.Mullaparthi@REDACTED Tue Oct 17 18:19:17 2000 From: Chandrashekhar.Mullaparthi@REDACTED (Chandrashekhar Mullaparthi) Date: Tue, 17 Oct 2000 17:19:17 +0100 Subject: No subject Message-ID: <402DD461F109D411977E0008C791C31202A796A1@imp02mbx.one2one.co.uk> NOTICE AND DISCLAIMER: This email (including attachments) is confidential. If you have received this email in error please notify the sender immediately and delete this email from your system without copying or disseminating it or placing any reliance upon its contents. We cannot accept liability for any breaches of confidence arising through use of email. Any opinions expressed in this email (including attachments) are those of the author and do not necessarily reflect our opinions. We will not accept responsibility for any commitments made by our employees outside the scope of our business. We do not warrant the accuracy or completeness of such information. From Sean.Hinde@REDACTED Tue Oct 17 20:08:56 2000 From: Sean.Hinde@REDACTED (Sean Hinde) Date: Tue, 17 Oct 2000 19:08:56 +0100 Subject: Lazy lists - an implementation. Message-ID: <402DD461F109D411977E0008C791C312565213@imp02mbx.one2one.co.uk> Hi, > > OK, since noone complained too much, here's a hack of lists.erl, > > where most functions have been enhanced to operate on both eager > > and lazy lists. > > And here is my first version of a lazy lists library. Seems > to work ok. Just so I (and I guess others?) understand how all this would be applied to the original problem (that iterating over very large lists causes large heaps to be generated).. If we already have a large list and want to generate another I guess there would there be no benefit of converting this into a lazy_list first? (would this make a Huge fun?) If we already have the list and want to do say a checksum calc on it (foreach/2) there is no advantage because we still have to run through all the elements (ok, one at a time)? So this could be used where the list itself doesn't already exist and we want to iterate to produce a result which isn't a list? Wouldn't the stack then fill up with the not yet calculated results of each iteration? My head is starting to spin. Under what circumstances would this stuff be very appropriate to use? Thanks for the code BTW - wow, Sean NOTICE AND DISCLAIMER: This email (including attachments) is confidential. If you have received this email in error please notify the sender immediately and delete this email from your system without copying or disseminating it or placing any reliance upon its contents. We cannot accept liability for any breaches of confidence arising through use of email. Any opinions expressed in this email (including attachments) are those of the author and do not necessarily reflect our opinions. We will not accept responsibility for any commitments made by our employees outside the scope of our business. We do not warrant the accuracy or completeness of such information. From swight@REDACTED Wed Oct 18 01:23:02 2000 From: swight@REDACTED (s. n. wight) Date: Tue, 17 Oct 2000 16:23:02 -0700 (PDT) Subject: Emacs mode "contribution"? Possibly... Message-ID: <14828.57174.792867.246322@bathory.verticalnet.com> Here are two VERY simple-minded elisp functions that insert the "-sname" option into the "inferior-erlang-machine-options" list which is fed to the erlang-shell function in Emacs when spawning new Erlang shells. The first, (erlang-set-nodename (nodename)) is an interactive (aka, runs in the mini-buf) method that inserts your "node name" choice into the current options list for the next (and ONLY the next) invocation of erlang-shell. The next shell that is spawned (either by compiling an Erlang buffer with "^C^K" or calling erlang-shell from the menu or mini-buf) will use that node name (in short form) and spawn a net_kernel distributed node. To prevent later confusion the second function, (erlang-clear-nodename ()), removes the node name directive from the options list; this is installed as an erlang-shell-mode-hook (in the example below). This insures that subsequent shells are started in "normal" singleton mode unless, of course, erlang-set-nodename is invoked with a new name. Both functions can be deliberately invoked from the mini-buffer if desired. I've found this to be a useful addition to the wonderful emacs-mode suite, when developing distributed applications. Regards, steve wight ===================================================================== ;; paste this block into your .emacs init file ;; ;; interactive function, run with M-x prior to shell creation ;; (defun erlang-set-nodename (nodename) "Prompts for distributed Erlang node name" (interactive "sNext Erlang node name shall be: ") (require 'erlang) (require 'cl) (let ((oldtail (member "-sname" inferior-erlang-machine-options))) (if oldtail (nsubstitute nodename (cadr oldtail) inferior-erlang-machine-options) (setq inferior-erlang-machine-options (cons "-sname" (cons nodename inferior-erlang-machine-options)))))) ;; ;; hook function to clean up after a shell is spawned - ;; may be run in mini-buf with M-x as well ;; (defun erlang-clear-nodename () "Hook function to delete Erlang node name from options list after use" (interactive) (let ((oldtail (member "-sname" inferior-erlang-machine-options))) (if oldtail (setq inferior-erlang-machine-options (remove "-sname" (remove (cadr oldtail) inferior-erlang-machine-options)))))) (add-hook 'erlang-shell-mode-hook 'erlang-clear-nodename) ===================================================================== From stimuli@REDACTED Wed Oct 18 01:47:39 2000 From: stimuli@REDACTED (Jeffrey Straszhiem) Date: Tue, 17 Oct 2000 19:47:39 -0400 Subject: Thoughts on laziness (long) Message-ID: <20001017194739.A1428@bzzt.shadow.net> Hi, Given all the recent discussion on lazy lists, I thought I'd make some comments. The availability of true call-by-need laziness is something that I think Erlang sorely lacks; in fact, I think it is the one useful mechanism that it most sorely lacks. First off, lazy lists are actually rather familiar objects to us programmers, even if we don't know it. For example, almost every language out there implements a rather specialized version of laziness called a "file stream". Think about it, a file stream is really a list of characters (or bytes) which are read lazily from the filesystem as needed. This "lazy list as stream" motif is well established. In the ML community, where lists are by default strict, laziness is provided through optional modules and called, you guessed it, "streams", and it does seem a useful terminology in languages, such as Erlang, where lists are by default strict. Now, some may think that adding laziness to the language is merely a matter of providing syntactic sugar on top of what can now be done with Funs, but this is not so. While a standard module for lazy lists as well as, perhaps, lazy list comprehension would be welcome, they don't answer the fundamental problem: Erlang's Funs only allow call-by-name style laziness, not the more powerful call-by-need. Call-by-name refers to a style of processing where the next element of a structure is passed as a closure (read function) instead of a data element. When the value is needed the closure is "forced" and the value thus obtained. For a stream, the function will return not only the next value, but also the next closure. A problem arises, however, when you force a closure twice, as you might if you pass the stream around to different functions, which may iterate over it multiple times. Each time a computation starts at the head of the list and iterates over it, the computation of the lists values must be performed anew. With a strict list the computation is only performed once. For some algorithms, of course, the data only needs to be processed once, and call-by-name laziness is fine. If you are using an algorithm that accesses the list multiple times (in part of whole) then strictness would serve better. There is another approach to laziness, however, which is used in Haskell and available in some versions of ML (and quite a few other systems), known as "call-by-need" laziness. In call-by-need laziness, a lazy closure is called a "suspension", and is only evaluated once. Then the value is remembered, and if that part of the stream is visited again, the previously used value is returned. With call-by-need laziness you are guaranteed that the computation will be performed at most once. So, call-by-need guarantees the same asomtoptic time efficiency as strict evaluation (although there is a constant time factor slowdown compared to strict evaluation). Call-by-need modifies values in the heap when a suspension is forced, and this may seem to violate functional purity. However, if you are thinking at the proper level of abstraction you will realize that this is not so. If you think of what is immutable as being the *value* of the suspension, the fact that is is computed later rather than sooner in no way "modifies" the value, even though behind the scenes pointers in the heap are being changed. That being said, the fact that call-by-need laziness does update the heap proves quite useful. In traditional strict languages, amortization methods prove impossible in algorithm analysis, as spread out modifications made by expensive operations over the course of several cheap operations. This sort of thing is not possible in a strict language, but there are tricks that can be done via the call-by-need mechanism to achieve it. I won't provide much detail here, but Chris Okasaki's _Purely Functional Data Structures_ discusses the topic at length. There exist implementations of queues, for instance, that have much better amortized behavior than what is possible in Erlang, particularly in cases where more than one computation iterates across the queue. Note that laziness does not always get better heap behavior than strictness. Depending on how you structure your list iteration, it is possible that pointers to the head of the list still exist during its entire lifetime. If this is so, then the garbage collector may not clean up the already processed nodes, even if they will not be visited again. Special compiler optimizations are sometime required to mitigate against this. Also, in many cases the ideal heap behavior is achieved by combining strictness and laziness. Haskell compilers, for instance, do extensive "strictness analysis" to find areas where strictness can be added to a computation to improve memory usage. This is the source of the claim that laziness can lead to bizzair heap behavior. It can, but in languages that are by default strict it is less of a problem with the few specifically lazy structures. And remember, with a lazy stream the worse case scenario is the same (within a constant factor) as a strict list: the entire list being created on the heap. Minus a constant factor, lazy streams will not have worse heap behavior than strict lists, and they might be very much better. Call-by-need laziness seems like a real winner. However, it would require compiler support to be used in Erlang, as behind the scenes it must perform updates on data elements, which is not possible in Erlang (at least not efficiently). Also, if one sees the value of lazy streams, then surely lazy trees, queues and other such are required. Other strict languages have provided laziness through a few simple primitives, such as Suspend and Force. Means to use these to construct lazy streams, and other such structures, are well covered in the literature. -- Jeffrey Straszheim | A sufficiently advanced -- Systems Engineer, Programmer | regular expression is -- http://www.shadow.net/~stimuli | indistinguishable from -- stimuli AT shadow DOT net | magic From aba3600@REDACTED Wed Oct 18 03:52:40 2000 From: aba3600@REDACTED (Andy with Recycled Electrons) Date: Tue, 17 Oct 2000 20:52:40 -0500 (CDT) Subject: Algorithmic lists In-Reply-To: Message-ID: > > Erlang? I'd love to overload "+" or "-" for a specific kind of input, > > so that a call is made to my function. The data units I am passing in > > are 3-element tuples right now, but I can change that. > > No, there isn't. (Or rather, yes, there is, it's called "parse > transforms", and you should really not be doing it.) My advice: implement > a nice abstract datatype with functions "add(X, Y)", "subtract(X, Y)", > etc., or if you really feel you want `+' and `-', you can actually call > your functions "'+'(X, Y)" and "'-'(X, Y)" as long as you use > single-quoting. Thanks to everyone who suggested some good ideas here. According to the group, there are 3 options: 1. Library Calls - I've got this wokring 2. Parse Transformations - My next step, but nobody recomends it 3. Built into Erlang - Not Possible I have a working data type implemented in library calls, and my goal is to implement infix function calls. I've done what I call "token level macros" before in Scheme. With "erl_scan" and "erl_parse" it looks possible...just a bit complex. Finally, does anyone have an interst in seeing a link to a Masters Thesis written in Erlang? It deals with robotics. Sincerely, Andy Allen Recycled Electrons email: andy@REDACTED From rv@REDACTED Wed Oct 18 10:02:29 2000 From: rv@REDACTED (Robert Virding) Date: Wed, 18 Oct 2000 10:02:29 +0200 Subject: Algorithmic lists In-Reply-To: Your message of "Tue, 17 Oct 2000 20:52:40 CDT." Message-ID: <200010180802.KAA19215@trana.bluetail.com> Andy with Recycled Electrons writes: > >Finally, does anyone have an interst in seeing a link to a Masters Thesis >written in Erlang? It deals with robotics. > Yes, yes, yes, definitely! Robert From etxuwig@REDACTED Wed Oct 18 10:41:23 2000 From: etxuwig@REDACTED (Ulf Wiger) Date: Wed, 18 Oct 2000 10:41:23 +0200 (MET DST) Subject: Lazy lists - an implementation. In-Reply-To: Message-ID: On Tue, 17 Oct 2000, Richard Carlsson wrote: >And here is my first version of a lazy lists library. Seems to work ok. >Nothing is evaluated unless necessary. Note, though, that my >interpretation of a lazy list is (like in the example that Tobbe gave, due >to Phil Wadler) a fun: > > LazyList :: () -> [] | [term() | LazyList] > >This makes things easier. The way Ulf implemented them can be seen as >using an explicit push-back buffer for an initial number of known >elements, but this makes programming with fully lazy lists more difficult, >because many more cases must be handled. (In my implementation, you can >push stuff back using e.g. `cons(H, T)' when necessary.) Also, in Ulfs >current implementation, either a list is empty or the first element has >been computed. (This could be fixed, though.) >From one perspective, it does make things easier. However, when we (at AXD 301) landed on the definition LazyList :: list() | [term() | fun()] it was partly because we wanted to stay as close to today's list syntax as possible - remember *our* objective to minimize the amount of code we have to rewrite, when switching between a list representation and a table representation. It's not immediately obvious to me how this can be achieved when a lazy list is "merely" a fun. I will do some thinking about this. /Uffe -- Ulf Wiger tfn: +46 8 719 81 95 Strategic Product & System Management mob: +46 70 519 81 95 Ericsson Telecom AB, Datacom Networks and IP Services Varuv?gen 9, ?lvsj?, S-126 25 Stockholm, Sweden From vladdu@REDACTED Wed Oct 18 10:51:07 2000 From: vladdu@REDACTED (Vlad Dumitrescu) Date: Wed, 18 Oct 2000 10:51:07 +0200 Subject: Windows build References: <39EAB965.27FC33EC@cellpt.com> <20001017151642.A25947@bluetail.com> Message-ID: > This spring I used Per's build environment to build otp_r6b > for win32. It's obviously much better to build on the > win32 machine directly than to build (and debug) in a mixed > adhoc environment. Use Cygnus gmake, openssl, perl MSVC++ nmake ... I have tried it in a hurry, just to see if it starts. There are however some error messages... /path.sh is missing how do I setup nmake under Cygnus? just by adding to the PATH? (I've never tried that before) some "cannot find target for..." messages, but maybe they come up because nmake is not found thanks /Vlad From klacke@REDACTED Wed Oct 18 10:55:22 2000 From: klacke@REDACTED (Klacke) Date: Wed, 18 Oct 2000 10:55:22 +0200 Subject: Windows build In-Reply-To: References: <39EAB965.27FC33EC@cellpt.com> <20001017151642.A25947@bluetail.com> Message-ID: <20001018105522.A1023@bluetail.com> On Tue, Oct 17, 2000 at 04:36:19PM +0200, Vlad Dumitrescu wrote: > > We aimed to ship our stuff on win32 as well > > as unices. (Un ...)fortunately this was cancelled so we never > > shiped. However here's the build_nt directory which I used. > > It's supposed to reside just under $ERL_TOP > > I will try it, thank you very much. > > A side note: It's a little confusing with all of you people at Bluetail that > keep referring to Ericsson as "we"! :-) > Well by "we" I don't mean ericsson at all, I mean Bluetail. When I say that "We aimed to ship .." I mean that bluetail aimed to ship a product (which just happens to be implemented in erlang) We've done all kind's of hackery to the otp system which we ship to the otp guys. They can choose to take it, ignore it or rewrite it ..... Cheers /klacke -- Claes Wikstrom Bluetail AB http://www.bluetail.com From vladdu@REDACTED Wed Oct 18 11:02:00 2000 From: vladdu@REDACTED (Vlad Dumitrescu) Date: Wed, 18 Oct 2000 11:02:00 +0200 Subject: Windows build References: <39EAB965.27FC33EC@cellpt.com> <20001017151642.A25947@bluetail.com> <20001018105522.A1023@bluetail.com> Message-ID: > Well by "we" I don't mean ericsson at all, I mean Bluetail. > When I say that "We aimed to ship .." I mean that bluetail > aimed to ship a product (which just happens to be implemented > in erlang) My mistake. I thought it was the shipping of Erlang/OTP that you meant. Vlad From rv@REDACTED Wed Oct 18 12:14:11 2000 From: rv@REDACTED (Robert Virding) Date: Wed, 18 Oct 2000 12:14:11 +0200 Subject: Lazy lists - an implementation. In-Reply-To: Your message of "Tue, 17 Oct 2000 17:55:30 +0200." Message-ID: <200010181014.MAA19625@trana.bluetail.com> Richard Carlsson writes: > ... Lots on lazy lists and a lazy list package. > I think that doing it as you have done making lazy lists as LazyList :: () -> [] | [term() | LazyList] makes things much simpler and more consistent. However you still have the major problem that it is not integrated with the rest of the whole system (Erlang, OTP and apps). This restricts what you can do with it, as the rest of the system sees this as funs not lists. As Sean Hinde points out if you already have the big list then why bother? Unfortunately it would not directly help Ulf Wiger either as the large lists are generated by calls which generate "proper" lists. To use it you would have to write new interface functions to generate the lazy lists and force people to use the lazy libraries. This would still mean you would have to go through code and modify it, as you would using th existing "lazy" interface to ETS tables. Most importantly it can't be used in patterns!!! As it can't be used interchangably with lists, or even list patterns, wouldn't it be better to drop the list terminology and instead call them something else, lazy sequences? This would help to lessen confusion. I wouldn't even construct them with lists, use a tagged tuple instead. I know it takes more space but I am only looking a one element at a time so it isn't really important. This would help to remove a lot of errors where people inadvertantly used "normal" list operations on them which could generate very random errors. And no way that I or anyone else would prefix their list procesing functions with a special clause! :-) func(LazyList, ...) when function(LazyList) -> func(LazyList(), ...); In short if you want to implement a "lazy" sequence be extremely explicit and not try to make look like lists, which they are not and don't behave as. Anyway without rewriting the result of an evaluation you can create an aweful amount of extra work. Also you could have fun code like L1 = eval(LazyList), L2 = eval(LazyList), and L1 /= L2. Robert -- Robert Virding Tel: +46 (0)8 545 55 017 Alteon Web Systems Email: rv@REDACTED S:t Eriksgatan 44 WWW: http://www.bluetail.com/~rv SE-112 34 Stockholm, SWEDEN "Folk s?ger att jag inte bryr mig om n?gonting, men det skiter jag i". From richardc@REDACTED Wed Oct 18 13:09:25 2000 From: richardc@REDACTED (Richard Carlsson) Date: Wed, 18 Oct 2000 13:09:25 +0200 (MET DST) Subject: Lazy lists - an implementation. In-Reply-To: <402DD461F109D411977E0008C791C312565213@imp02mbx.one2one.co.uk> Message-ID: On Tue, 17 Oct 2000, Sean Hinde wrote: > > And here is my first version of a lazy lists library. Seems > > to work ok. > > Just so I (and I guess others?) understand how all this would be > applied to the original problem (that iterating over very large lists > causes large heaps to be generated).. > > If we already have a large list and want to generate another I guess > there would there be no benefit of converting this into a lazy_list > first? (would this make a Huge fun?) Not "Huge" - the function "lazy(L)" takes your normal list and embeds it in a small fun (no copying done) which will on demand return the next element. The space usage is comparable to a tuple with a small number of elements. > If we already have the list and want to do say a checksum calc on it > (foreach/2) there is no advantage because we still have to run through > all the elements (ok, one at a time)? Yes. > So this could be used where the list itself doesn't already exist and > we want to iterate to produce a result which isn't a list? Wouldn't > the stack then fill up with the not yet calculated results of each > iteration? Well, what the result is (list or not) is not important as such, and an iteration like the following executes in constant space: sum(L) -> sum(L, 0). sum(L, A) -> case L() of [H | T] -> sum(T, A + H); [] -> A end. What happens is that the "lazy list" will never really be built in memory as a list - the producer just uses cons cells to pass the next element on demand (the "tail" is the function to use for getting the element after that, and so forth), and [] to signal "the end". > My head is starting to spin. Under what circumstances would this stuff > be very appropriate to use? For one thing, if you were to have a "final consumer" of some list (or "stream"), which would take elements one at a time and use them for something, bluntly assuming that they are right for its task, and you have some producer that has passed you a lazy list (stream), and you want to apply some modification to the elements (map, filter, merge, ...) - but you don't want to read the whole list, do your thing to it, and send the result to the consumer - you just want to "attach" the modification without requesting a single element before the consumer decides that it wants one. Using a lazy list: transmogrify(L) -> L1 = lazy_lists:map(fun my_filter/1, L), consumer(L1). Now, the "map" will be applied to each element as the consumer requests them, and not until then. You can in this way attach any sort of complicated modification to a lazy list (stream) without having to pass callback hooks to the consumer through a complicated interface. You just pass your new lazy list. /Richard Richard Carlsson (richardc@REDACTED) (This space intentionally left blank.) E-mail: Richard.Carlsson@REDACTED WWW: http://www.csd.uu.se/~richardc/ From rv@REDACTED Wed Oct 18 13:11:44 2000 From: rv@REDACTED (Robert Virding) Date: Wed, 18 Oct 2000 13:11:44 +0200 Subject: Thoughts on laziness (long) In-Reply-To: Your message of "Tue, 17 Oct 2000 19:47:39 EDT." <20001017194739.A1428@bzzt.shadow.net> Message-ID: <200010181111.NAA19866@trana.bluetail.com> Jeffrey Straszhiem writes: >Hi, ... Long discussion on call by need laziness > I personally think that laziness can be interesting but have yet come across any applications in Erlang where it would have been useful. Many of the uses of laziness in other functional languages are handled in Erlang with processes and message communication. Implementing lazy streams for communication does not give you very much and in many cases is much more limiting than Erlang's message passing with selective receives. Another problem with call-by-need, which I tried to point out in an earlier mail, is that it creates problems when used in conjunction with processes and distribution. For many applicattions it IS important WHEN and WHERE things are done. You also have the proble that in Erlang programs there are many functiona calls which are done for effect, send a message, so waiting to evaluate them until you need the result won't give you much. :-) My solution was, as mentioned, force full evalutaion for all messages and for all calls which ignore the result. This should make the most things work, but you will still end up with sequencing problems: R1 = foo(...), R2 = bar(...), If R1 and R2 are used then both foo nad bar will be called but I may lose control of the order, which may be important. This is similar to the problems we had when implementing Erlang on top of Strand, a parallel logic language, where I had to pass around a state variable and wait for it everywhere just to be sure of sequencing. I agree, however, that to make this work properly it would have to be proper call-by-need with updating of the heap when a result has been evaluated. However, I wonder if it possible to update Erlang with such a feature and still keep it sufficiently backwards compatible to call be able to call it the same language. You might end up with a new language, Lazy Erlang, with new semantics. Robert -- Robert Virding Tel: +46 (0)8 545 55 017 Alteon Web Systems Email: rv@REDACTED S:t Eriksgatan 44 WWW: http://www.bluetail.com/~rv SE-112 34 Stockholm, SWEDEN "Folk s?ger att jag inte bryr mig om n?gonting, men det skiter jag i". From Sean.Hinde@REDACTED Wed Oct 18 13:09:07 2000 From: Sean.Hinde@REDACTED (Sean Hinde) Date: Wed, 18 Oct 2000 12:09:07 +0100 Subject: Lazy lists - an implementation. Message-ID: <402DD461F109D411977E0008C791C31256521D@imp02mbx.one2one.co.uk> > Unfortunately it would not directly help Ulf Wiger either as the large > lists are generated by calls which generate "proper" lists. To use it > you would have to write new interface functions to generate the lazy > lists and force people to use the lazy libraries. This would still > mean you would have to go through code and modify it, as you would > using th existing "lazy" interface to ETS tables. mnesia transactions seem to use pretty much this procedure (generate list, convert to ets table, traverse ets table to generate new list - or something similar). Obviously the author had some reason to choose this solution. Maybe there's a better one which doesn't involve all that copying but also doesn't have the downside which Ulf and the mnesia author are trying to avoid (different memory allocation? different GC? different handling of fast growing heaps? other?) Sean NOTICE AND DISCLAIMER: This email (including attachments) is confidential. If you have received this email in error please notify the sender immediately and delete this email from your system without copying or disseminating it or placing any reliance upon its contents. We cannot accept liability for any breaches of confidence arising through use of email. Any opinions expressed in this email (including attachments) are those of the author and do not necessarily reflect our opinions. We will not accept responsibility for any commitments made by our employees outside the scope of our business. We do not warrant the accuracy or completeness of such information. From rv@REDACTED Wed Oct 18 13:25:50 2000 From: rv@REDACTED (Robert Virding) Date: Wed, 18 Oct 2000 13:25:50 +0200 Subject: Lazy lists - an implementation. In-Reply-To: Your message of "Wed, 18 Oct 2000 13:09:25 +0200." Message-ID: <200010181125.NAA19963@trana.bluetail.com> Richard Carlsson writes: > >On Tue, 17 Oct 2000, Sean Hinde wrote: > >> If we already have a large list and want to generate another I guess >> there would there be no benefit of converting this into a lazy_list >> first? (would this make a Huge fun?) > >Not "Huge" - the function "lazy(L)" takes your normal list and embeds it >in a small fun (no copying done) which will on demand return the next >element. The space usage is comparable to a tuple with a small number of >elements. Yes, but you would still already have created the original huge list. Converting it to a lazy list AFTERWARDS will not gain you anything in space, actually it will cost you as you replace a cons cell with a tuple of a few extra elements. That added to the extra execution cost of first making it lazy and then extracting each cons cell makes it all seem like a big lose. This means that using lazy lists is really only practical if you use it consistently from start to finnish with an object. That is why I suggested not calling them lazy lists but lazy as they aren't interchangeable. Robert -- Robert Virding Tel: +46 (0)8 545 55 017 Alteon Web Systems Email: rv@REDACTED S:t Eriksgatan 44 WWW: http://www.bluetail.com/~rv SE-112 34 Stockholm, SWEDEN "Folk s?ger att jag inte bryr mig om n?gonting, men det skiter jag i". From klacke@REDACTED Wed Oct 18 13:41:30 2000 From: klacke@REDACTED (Klacke) Date: Wed, 18 Oct 2000 13:41:30 +0200 Subject: Windows build In-Reply-To: References: <39EAB965.27FC33EC@cellpt.com> <20001017151642.A25947@bluetail.com> Message-ID: <20001018134130.A10859@bluetail.com> There's a whole lot of win32 specific stuff that need to be setup properly before this thing works. It most certainly doesn't work outofthebox asis. Maybe there were some small fixes to erts too, I don't remember. Path's to MS compiler (version??) etc to work standalone/commandline, Cygnus (1.0 needs to be installed directly under C:), ... etc If compiling openssl, we need ActivePerl first in the path, compiling erts need Cygnus perl first in the path.... These were things from the top of my head, there were probably more, and as I said, we dropped it and I no longer work on it. > I have tried it in a hurry, just to see if it starts. There are however some > error messages... > > /path.sh is missing ignore this .. (I think) > > how do I setup nmake under Cygnus? just by adding to the PATH? (I've never > tried that before) Basically yes. Install the MS compiler and see what _it_ adds to the path in the registry and copy that. As well as all the cygnus path's as usual. > > some "cannot find target for..." messages, but maybe they come up because > nmake is not found > probably Good luck /klacke -- Claes Wikstrom Bluetail AB http://www.bluetail.com From richardc@REDACTED Wed Oct 18 14:00:36 2000 From: richardc@REDACTED (Richard Carlsson) Date: Wed, 18 Oct 2000 14:00:36 +0200 (MET DST) Subject: Lazy lists - an implementation. In-Reply-To: <200010181014.MAA19625@trana.bluetail.com> Message-ID: On Wed, 18 Oct 2000, Robert Virding wrote: > I think that doing it as you have done making lazy lists as > > LazyList :: () -> [] | [term() | LazyList] > > makes things much simpler and more consistent. > > However you still have the major problem that it is not integrated > with the rest of the whole system (Erlang, OTP and apps). This > restricts what you can do with it, as the rest of the system sees this > as funs not lists. Yes, it is not a list. Should not even try to be a list. (I just implemented them using cons cells because I liked the notation, but that is trivial to change.) I see all this as a question of interfaces between functions. As I just wrote in reply to Seans question, a lazy data structure allows you to attach computations without passing an explicit callback hook. In some applications and programming styles, this might be precisely what you want. In others not. I think that functions now operating on normal lists should *not* be augmented to also handle lazy lists, simply because it mucks up their otherwise simple and straightforward interface using normal lists. If one wants lazy data structures, one should have completely disjoint functions for operating on them. The programmer should be able to know what might and might not happen. I am actually tiring of the term "lazy lists", so I'm now switching to "streams", and I hope that no-one will confuse this with Erlangs I/O streams (which are a process communication protocol). > As Sean Hinde points out if you already have the big list then why > bother? Only if you want to use an existing list with a function that wants a stream as input. > Unfortunately it would not directly help Ulf Wiger either as the large > lists are generated by calls which generate "proper" lists. To use it > you would have to write new interface functions to generate the lazy > lists and force people to use the lazy libraries. This would still > mean you would have to go through code and modify it, as you would > using th existing "lazy" interface to ETS tables. Yes. But I think that the sort of "damage control" that Ulf wanted will in the end make the code deteriorate into something unreadable, bug-prone and unpredictable. Better rewrite the code once and for all into something that can be read and understood, if you decide that you want to use this sort of technique. > Most importantly it can't be used in patterns!!! Well, do you want to? If you want to look at the first N elements of a stream, you just extract them and then do the matching. There is an infinite number of possible abstract data types that you cannot do matching on. Give us O'Keefes abstract patterns, and the whole problem might go away. > As it can't be used interchangably with lists, or even list patterns, > wouldn't it be better to drop the list terminology and instead call > them something else, lazy sequences? This would help to lessen > confusion. I agree. I'll stick to "streams" from now on. > I wouldn't even construct them with lists, use a tagged tuple instead. > I know it takes more space but I am only looking a one element at a > time so it isn't really important. This would help to remove a lot of > errors where people inadvertantly used "normal" list operations on > them which could generate very random errors. Yes, you may well be right. (Me, I don't make errors like that. :-). > And no way that I or anyone else would prefix their list procesing > functions with a special clause! :-) > > func(LazyList, ...) when function(LazyList) -> func(LazyList(), ...); No, as I said, I do absolutely not want to augment code handling normal lists with extra cases for continuations. That way lies madness! > In short if you want to implement a "lazy" sequence be extremely > explicit and not try to make look like lists, which they are not and > don't behave as. OK, boss. > Anyway without rewriting the result of an evaluation you can create an > aweful amount of extra work. Also you could have fun code like > > L1 = eval(LazyList), > L2 = eval(LazyList), > > and L1 /= L2. Yes, this is the major snag, so to speak. One would like the advantages of computing things at most once, without the disadvantages of having to explicitly pass around the "updated stream" to anyone who might want to read it again from the same point. As you also said in your "philosophical questions" letter, this laziness stuff is already handled well by process communication (as e.g., I/O streams), which also makes it clear who computes what and when. "Lazy list" streams should only be used if they offer something that makes the task at hand a lot easier, and you are prepared for the effects of lazy evaluation. The advantages of "lazy list" streams over message passing are then 1) locality (no copying of data between processes) - but this might go away *if* we get an Erlang implementation with a global heap, and 2) the simple but powerful interface, allowing you to modify a stream as you like, but also postponing the computation to another time (and possibly another process). Actually, I can't say I have a huge use for them myself, but I like to explore the idea. Real uses may crop up later. /Richard Richard Carlsson (richardc@REDACTED) (This space intentionally left blank.) E-mail: Richard.Carlsson@REDACTED WWW: http://www.csd.uu.se/~richardc/ From richardc@REDACTED Wed Oct 18 14:13:12 2000 From: richardc@REDACTED (Richard Carlsson) Date: Wed, 18 Oct 2000 14:13:12 +0200 (MET DST) Subject: Lazy lists - an implementation. In-Reply-To: <200010181125.NAA19963@trana.bluetail.com> Message-ID: On Wed, 18 Oct 2000, Robert Virding wrote: > Richard Carlsson writes: > > > >On Tue, 17 Oct 2000, Sean Hinde wrote: > > > >> If we already have a large list and want to generate another I guess > >> there would there be no benefit of converting this into a lazy_list > >> first? (would this make a Huge fun?) > > > >Not "Huge" - the function "lazy(L)" takes your normal list and embeds it > >in a small fun (no copying done) which will on demand return the next > >element. The space usage is comparable to a tuple with a small number of > >elements. > > Yes, but you would still already have created the original huge list. > Converting it to a lazy list AFTERWARDS will not gain you anything in > space, actually it will cost you as you replace a cons cell with a > tuple of a few extra elements. That added to the extra execution cost > of first making it lazy and then extracting each cons cell makes it all > seem like a big lose. I'm sorry for being unclear - that was not what I was trying to convey - only that if you do have a normal list and you *want* to use it as a stream (for whatever reason) you do not get a big space penalty for it. Of course, you cannot make an existing huge list vanish by wrapping it in a closure. /Richard Richard Carlsson (richardc@REDACTED) (This space intentionally left blank.) E-mail: Richard.Carlsson@REDACTED WWW: http://www.csd.uu.se/~richardc/ From etxuwig@REDACTED Wed Oct 18 14:39:40 2000 From: etxuwig@REDACTED (Ulf Wiger) Date: Wed, 18 Oct 2000 14:39:40 +0200 (MET DST) Subject: Lazy lists - an implementation. In-Reply-To: Message-ID: On Wed, 18 Oct 2000, Richard Carlsson wrote: >Yes. But I think that the sort of "damage control" that Ulf wanted >will in the end make the code deteriorate into something unreadable, >bug-prone and unpredictable. Better rewrite the code once and for >all into something that can be read and understood, if you decide >that you want to use this sort of technique. I wrote the following in a previous post. "Of course, these rewrites are usually warranted for other reasons, but timing is always an issue in production projects. If we can, we'd like to schedule complete rewrites to some other time than during the system test phase -- which is a fairly common place to find these problems." (http://www.erlang.org/ml-archive/erlang-questions/200010/msg00135.html) In practically all the circumstances where we've come across the poor memory management behaviour that would make the code a candidate for this type of reprogramming, we've also recommended quite assertively that the code be rewritten, usually for a variety of other reasons as well. Always, we get the same response: "sure we'd like to rewrite the code, but there simply isn't time." This is the reality of complex product development projects in a fiercely competitive environment: you can very seldomly afford a major rewrite of code, so techniques for partial rewrites become very interesting. We *have* performed major rewrites on a number of occasions - even performed a total overhaul of a major part of the system. In all cases where we've done this, it's been a success, but it is also a huge undertaking - even in Erlang. >No, as I said, I do absolutely not want to augment code handling normal >lists with extra cases for continuations. That way lies madness! We must be thinking of totally different problems. For the problems I'm trying to solve, augmenting the list metaphor seems like the logical way to go - certainly not madness. Anyway, after talking to Bj?rn G. on the phone, I'm prepared to drop my suggestion in exchange for the following: - functions like map, foldl, etc. on ets and mnesia tables - list comprehensions on ets tables This would bring us closer to our goal of making partial rewrites much easier, while avoiding the philosophical woes that seem to surface as soon as lazy evaluation is mentioned. /Uffe -- Ulf Wiger tfn: +46 8 719 81 95 Strategic Product & System Management mob: +46 70 519 81 95 Ericsson Telecom AB, Datacom Networks and IP Services Varuv?gen 9, ?lvsj?, S-126 25 Stockholm, Sweden From thomasl@REDACTED Wed Oct 18 14:59:26 2000 From: thomasl@REDACTED (Thomas Lindgren) Date: Wed, 18 Oct 2000 14:59:26 +0200 Subject: Lazy lists - an implementation. In-Reply-To: (message from Ulf Wiger on Wed, 18 Oct 2000 14:39:40 +0200 (MET DST)) Message-ID: <200010181259.OAA01775@lammgam.bluetail.com> > Anyway, after talking to Bj?rn G. on the phone, I'm prepared to drop > my suggestion in exchange for the following: > > - functions like map, foldl, etc. on ets and mnesia tables > - list comprehensions on ets tables This sounds much more attractive than generic lazy lists to me. And mnemosyne could benefit as well. There are interesting possibilities in allowing various definitions of list comprehension generators, not just adding a first/next generator. For example, I'd like to add these operations over binaries as well (that is, compiling the generating expression intelligently; not just with an implicit binary_to_list). The function binary:foldl(fun crc/2,0,Bin) could _efficiently_ compute the CRC of a binary, without converting to a list. (Using the default 8b size.) Finally, a compiler technique that potentially subsumes all of the above is deforestation: it is capable of collapsing multiple traversals over a list into a single one. As an example, first consider the slow program: ---------------------------------------------------------------------- %% collect all keys, then process some prefix of them process_keys_of_table(P,F,Table) -> process_keys_until(P,F,collect_all_keys(Table)). %% collect all keys of a table into a list collect_all_keys(Table) -> collect_loop(Table,ets:first(Table)). collect_loop(Tab,'$end_of_table') -> []; collect_loop(Tab,Key) -> [Key|collect_loop(ets:next(Tab,Key))]. %% process the keys of a list until P is false process_keys_until(P,F,Table,[Key|Keys]) -> case P(Table,Key) of true -> [F(Table,Key)|process_keys_until(P,F,Keys)]; false -> [] end. ---------------------------------------------------------------------- This can be _automatically_ rewritten by deforestation (with some restrictions on P and F) into: ---------------------------------------------------------------------- process_keys_of_table(P,F,Table) -> p_c(P,F,Table,ets:first(Table)). %% p_c traverses the keys until P(Key) is no longer true p_c(P,F,Table,'$end_of_table') -> []; p_c(P,F,Table,Key) -> case P(Table,Key) of true -> [F(Table,Key)|p_c(P,F,Table,ets:next(Table))]; false -> [] end. ---------------------------------------------------------------------- Nice, huh? There was a masters thesis working on applying deforestation to Erlang at Uppsala a couple of years back. (Unfortunately, it got derailed and ended up not being about Erlang at all.) I think there is some scope for sophisticated compilation/program transformation here. Deforestation is an attractive case. Thomas -- Thomas Lindgren thomasl+junk@REDACTED Alteon Websystems Sweden http://www.bluetail.com "The need of a constantly expanding market for its products chases the bourgeoisie over the entire surface of the globe. It must nestle everywhere, settle everywhere, establish connections everywhere." -- Communist Manifesto From vladdu@REDACTED Wed Oct 18 16:04:22 2000 From: vladdu@REDACTED (Vlad Dumitrescu) Date: Wed, 18 Oct 2000 16:04:22 +0200 Subject: Windows build Message-ID: After hitting many times my head against the walls, I managed to compile almost everything. There weren't many things to fix, but I'm unused to this kind of build, being a slave to the IDEs... :-) The sizes of the executables don't match the release sizes, but I can run simple stuff in the shell ! What doesn't work, and I can't figure why (partly because I'm very hungry :-) - erlsrv.exe doesn't build. Under Gygwin, Nmake just says "don't know how to make", but if running from NT directly, it says " cycle in dependency tree"... strange.... - the libraries don't build. It seems that the makefiles don't get the needed environment. For example EMULATOR is empty and make reports "no rule to make target" if defining it as 'beam' - the start scripts don't build. "pa" is not found - probably SASL_PATH gets screwed up somehow Can someone hint me in the right direction? I will try again some other day and will send back the adjustments to the build files then. regards Vlad -------------- next part -------------- An HTML attachment was scrubbed... URL: From vladdu@REDACTED Wed Oct 18 16:15:35 2000 From: vladdu@REDACTED (Vlad Dumitrescu) Date: Wed, 18 Oct 2000 16:15:35 +0200 Subject: Windows build Message-ID: ignore erlsrv.exe, it was a simple dumb mistake from my part... just a backslash too many... :-\ -------------- next part -------------- An HTML attachment was scrubbed... URL: From richardc@REDACTED Wed Oct 18 16:55:28 2000 From: richardc@REDACTED (Richard Carlsson) Date: Wed, 18 Oct 2000 16:55:28 +0200 (MET DST) Subject: Streams/lazy lists In-Reply-To: Message-ID: Well, if anyone is still keen on playing around with these things, I edited the implementation a bit: now called "streams" and uses tuples instead of cons cells. Enjoy. /Richard Richard Carlsson (richardc@REDACTED) (This space intentionally left blank.) E-mail: Richard.Carlsson@REDACTED WWW: http://www.csd.uu.se/~richardc/ -------------- next part -------------- %% =============================================================== %% Lazy streams %% %% Copyright (C) 2000 Richard Carlsson %% %% This library is free software; you can redistribute it and/or %% modify it under the terms of the GNU Lesser General Public %% License as published by the Free Software Foundation; either %% version 2 of the License, or (at your option) any later %% version. %% %% This library is distributed in the hope that it will be useful, %% but WITHOUT ANY WARRANTY; without even the implied warranty of %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the %% GNU Lesser General Public License for more details. %% %% You should have received a copy of the GNU Lesser General %% Public License along with this library; if not, write to the %% Free Software Foundation, Inc., 59 Temple Place, Suite 330, %% Boston, MA 02111-1307 USA %% %% Author contact: richardc@REDACTED %% =============================================================== -module(streams). -vsn('$Id: streams.erl,v 1.2 2000/10/18 13:58:07 richardc Exp $'). -author('richardc@REDACTED'). -copyright('Copyright (C) 2000 Richard Carlsson'). -export([append/2, constant/1, drop/2, duplicate/2, empty/0, filter/2, first/2, foldl/3, foldr/3, foreach/2, integers/1, map/2, member/2, merge/3, nth/2, nthtail/2, push/2, random/0, random_integers/1, seq/2, seq/3, stream/1, subsequence/3, to_list/1]). %% The type of the streams defined below is: %% %% Stream :: () -> {} | {term(), Stream} %% %% A stream is thus always a fun. One advantage is that if a %% producer has side effects (e.g., if it reads elements from a %% file), we need never trigger the next effect unless we really %% want to see the next element. Also, programming becomes very %% straightforward. Let's hope for better compilers to make this %% sort of programming more efficient. %% =============================================================== %% The following functions compute new streams without forcing %% evaluation of their input. %% The empty stream empty() -> fun () -> {} end. %% The stream consisting of H followed by all elements of T. push(H, T) -> fun () -> {H, T} end. %% Converts a stream into a list. Make sure never to pass an %% infinite stream to this function! to_list(S) -> case S() of {H, T} -> [H | to_list(T)]; {} -> [] end. %% Turns a proper normal list into a stream. stream(L) -> fun () -> case L of [H | T] -> {H, stream(T)}; [] -> {} end end. %% The stream of all elements of S1 followed by all elements of %% S2. append(S1, S2) -> fun () -> case S1() of {} -> S2(); {H, T} -> {H, append(T, S2)} end end. %% The stream of integers in the interval [From, To], in ascending %% order. seq(From, To) -> seq(From, To, 1). %% The stream of integers [From, From + D, From + 2*D, ..., From + %% D*((To - From) mod D)]. The interval is empty if D does not %% have the same sign as the difference To - From. seq(From, To, D) when integer(From), integer(To), From < To, D > 0 -> fun () -> {From, seq(From + D, To, D)} end; seq(From, To, D) when integer(From), integer(To), To < From, D < 0 -> fun () -> {From, seq(From + D, To, D)} end; seq(From, To, D) when integer(From), integer(To), To == From -> fun () -> {From, empty()} end; seq(From, To, D) -> empty(). %% The stream of integers starting at N. integers(N) -> fun () -> {N, integers(N + 1)} end. %% The infinite stream of elements X. constant(X) -> fun () -> {X, constant(X)} end. %% The stream of length N, where each element is X. duplicate(N, X) when integer(N) -> fun () -> if N > 0 -> {X, duplicate(N - 1, X)}; N == 0 -> {} end end. %% An infinite stream of random floating-point numbers in the %% interval [0.0, 1.0]. `random:seed' must be used (by the same %% process) to set the seed for the random number generator before %% this function is called. random() -> fun () -> {random:uniform(), random()} end. %% An infinite stream of random integers in the interval [0, Max]. %% See `random' above for details. random_integers(Max) when Max >= 1 -> fun () -> {random:uniform(Max), random_integers(Max)} end. %% The stream consisting of the first N elements of S, or the %% stream S if the length of S is not greater than N. first(0, S) -> empty(); first(N, S) when integer(N), N > 0 -> fun () -> case S() of {H, T} -> {H, first(N - 1, T)}; {} -> {} end end. %% The stream [EN+1, EN+2, ...], if S is [E1, ..., EN, EN+1, ...]. drop(0, S) -> S; drop(N, S) when integer(N), N > 0 -> fun () -> case S() of {H, T} -> (drop(N - 1, T))(); {} -> {} end end. %% The stream [EN, EN+1, ..., EN+D], if S is [E1, E2, ...]. subsequence(N, D, S) when N > 0, D >= 0 -> first(D, drop(N, S)). %% The stream [F(E1), F(E2), F(E3), ...] if S is [E1, E2, E3, %% ...]. map(F, S) -> fun () -> case S() of {H, T} -> {F(H), map(F, T)}; {} -> {} end end. %% The stream of all elements E in S (in the same order) for which %% P(E) returns `true'. P must return either `true' or `false' for %% all elements in S. filter(P, S) -> fun () -> case S() of {H, T} -> case P(H) of true -> {H, filter(P, T)}; false -> (filter(P, T))() end; {} -> {} end end. %% Returns the stream of elements in S1 and S2 where the %% respective relative order of elements is preserved, and each %% element Y of S2 is ordered before the first possible X of S1 %% such that P(X, Y) yields `false'. P(X, Y) must yield either %% `true' or `false' for all X in S1 and Y in S2. (P can be read %% as "less than".) merge(P, S1, S2) -> fun () -> case S1() of {H1, T1} -> case S2() of {H2, T2} -> case P(H1, H2) of true -> {H1, merge(P, T1, push(H2, T2))}; false -> {H2, merge(P, push(H1, T1), T2)} end; {} -> {H1, T1} end; {} -> S2() end end. %% =============================================================== %% All functions below may evaluate all or part of their input. %% Returns `true' if X is in the stream S, and `false' otherwise. member(X, S) -> case S() of {H, T} -> if H == X -> true; true -> member(X, T) end; {} -> false end. %% Returns the Nth element of stream S. nth(N, S) -> S1 = S(), if N > 1 -> nth(N - 1, tl(S1)); true -> hd(S1) end. %% Returns the stream [AN+1, AN+2, ...] if S is [A1, A2, ..., AN, %% AN+1, AN+2, ...]. nthtail(N, S) when N > 1 -> nthtail(N - 1, tl(S())); nthtail(0, S) -> S. %% Computes (...((A F E1) F E2)... F EN), if S is [E1, E2, ..., %% EN] and F is a binary function (here written as an infix %% operator). foldl(A, F, S) -> case S() of {H, T} -> foldl(F(A, H), F, T); {} -> A end. %% Computes (E1 F ...(EN-1 F (EN F A))...), if S is [E1, E2, ..., %% EN] and F is a binary function (here written as an infix %% operator). foldr(S, F, A) -> case S() of {H, T} -> F(H, foldr(T, F, A)); {} -> A end. %% Evaluates F(E1), F(E2), ..., if S is [E1, E2, ...]. Always %% returns `ok'. foreach(F, S) -> case S() of {H, T} -> F(H), foreach(F, T); {} -> ok end. %% =============================================================== From hakan@REDACTED Wed Oct 18 16:59:29 2000 From: hakan@REDACTED (Hakan Mattsson) Date: Wed, 18 Oct 2000 16:59:29 +0200 (MET DST) Subject: Lazy lists - an implementation. In-Reply-To: <402DD461F109D411977E0008C791C31256521D@imp02mbx.one2one.co.uk> Message-ID: On Wed, 18 Oct 2000, Sean Hinde wrote: Sean> > Unfortunately it would not directly help Ulf Wiger either as the large Sean> > lists are generated by calls which generate "proper" lists. To use it Sean> > you would have to write new interface functions to generate the lazy Sean> > lists and force people to use the lazy libraries. This would still Sean> > mean you would have to go through code and modify it, as you would Sean> > using th existing "lazy" interface to ETS tables. Sean> Sean> mnesia transactions seem to use pretty much this procedure (generate list, Sean> convert to ets table, traverse ets table to generate new list - or something Sean> similar). Obviously the author had some reason to choose this solution. Similar? Updates in a Mnesia transaction are stored in a temporary ets table. At commit time, the transaction coordinator iterates over the transaction storage once. During this iteration several new lists are generated (one list per storage type and node). These lists (embedded in records) are then sent to the other involved nodes, possibly logged to file and finally written to ets/dets depending on the storage type. Ok, there are lots of copying involved here but I don't think that this part of Mnesia could be made so much more efficient without new features in erts. Sean> Maybe there's a better one which doesn't involve all that copying but also Sean> doesn't have the downside which Ulf and the mnesia author are trying to Sean> avoid (different memory allocation? different GC? different handling of fast Sean> growing heaps? other?) I don't know if Mnesia would benefit of lazyness, since the entire application by nature is a large side effect. But there are other, less sexy, things that it would benefit of. Mnesia would benefit of hash tables locally allocated on the process heap. The effect of this would be twofold. Firstly, it would avoid the copying of data between the process heap and the ets storage, for each access of the table. Secondly it would make the disposal of the temporary hash table much cheaper. Allocating tables on the process heap would also enable experiments with more adaptive data structures, where plain lists could transparently be transformed into tables, trees, tuples etc. depending on (deduced or explicitly stated) access patterns and data sizes. A memory allocation stategy with separate heaps (garbage collected or not) for each table would give yet other set of characteristics than the current implementation with a global table heap. Some clever multi module flow analysis, would enable a wide range of optimisations, such as avoidance of external calls, destructive updates of records, etc. etc. Currently, there is an unfair performance cost for well structured applications, as the compiler only operates at one module at the time and many optimizations only are performed within one function. Modules that not are intended to be used from other applications should be for free. /H?kan --- H?kan Mattsson Ericsson Computer Science Laboratory http://www.ericsson.se/cslab/~hakan From thomasl@REDACTED Wed Oct 18 17:54:53 2000 From: thomasl@REDACTED (Thomas Lindgren) Date: Wed, 18 Oct 2000 17:54:53 +0200 Subject: Lazy lists - an implementation. In-Reply-To: (message from Hakan Mattsson on Wed, 18 Oct 2000 16:59:29 +0200 (MET DST)) References: Message-ID: <200010181554.RAA02374@lammgam.bluetail.com> > Some clever multi module flow analysis, would enable a wide range of > optimisations, such as avoidance of external calls, destructive > updates of records, etc. etc. Currently, there is an unfair > performance cost for well structured applications, as the compiler > only operates at one module at the time and many optimizations only > are performed within one function. Modules that not are intended to > be used from other applications should be for free. Coming soon ... currently at the research stage, with a clunkily running prototype. And yes, I am a firm believer in the power of cross-module optimization. One solution is to abandon hot code loading, or to modify it so that some modules must be loaded as a group. (See also: ftp://ftp.csd.uu.se/pub/papers/reports/0154.ps.gz for some background.) OK, since this is a favorite topic of mine, hit 'n' or stay around for a longish discussion. Here we go. I know of two implementations of "module merging" that can also group modules into a big 'super module': one by Richard Carlsson and one by me. Module merging is intended to be safe with the current hot code loading semantics, but you can also (basically) just 'cat' all the modules to be merged into a single big file and compile that. The devil is in the details, however. First of all, you have to handle apply/spawn/spawn_link and similar stuff. Second, exports have to be accomodated somehow (in the best of worlds, a file should be able to export several interfaces, but right now it can't). But the main problem at this time, if we work at the source level, is that of records: - We don't want to expand records into tuples, since that carries less information for the compiler to work with. So we have to keep the record operations around. - Two record definitions in different modules can have the same name. (An example of this is mnesia, where the name 'state' is used for several different record definitions.) - This means the files can't be straightforwardly merged since the names may clash and the compiler can't handle that. - On the other hand, we can't simply give each record definition a unique names, since the same record can be used in several places (file.hrl, say, included here and there) and all those uses must have the same name. The current solution is this: records are renamed to have a name that is unique _per_unique_definition_, not per occurrence. This is done by constructing an MD5 hash of the fields and their initializers, and appending that to the name. All equivalent record definitions (same name, same fields) will then get the same name. Thus, if your record looks like -record(state, {supervisor, pid_tab}). You then get a new, unique name based on the MD5 of the definition: 'state 2d7265636f72642873746174652c207b73757065727669736f722c7069645f7461627d' (The 'state ' prefix is retained just to keep things debuggable -- the MD5 is sufficient.) Now, if the same record definition occurs in several places then all those definitions will have the same renaming. There are drawback: first, printing the record looks weird (the tag is 'record '); second, the _entire_ system must use this MD5 renaming scheme. Otherwise, some modules will still use the old naming scheme. Still, that's not too onerous. And when records are Done Right, this problem will go away. Wishlist: - records to have unique names over the entire system, etc. * source-to-source transform has to go - export many interfaces * would also be nice to have -apply([f/1]), -spawn([g/2]), etc to make it easier to see what is exported just for those purposes; this would also get rid of the comment "internal export". - better support for detecting module names in code (for instance, gen_tcp stores an atom in a table, which is later used as a module name ... there's little hope of detecting that and changing the name into optimized_gen_tcp, say) - no _undocumented_ magic BIFs in os.erl and similar (+ some way to detect those programmatically), since renaming os.erl into optimized_os.erl will fool the runtime system. Thomas -- Thomas Lindgren thomasl+junk@REDACTED Alteon Websystems Sweden http://www.bluetail.com "The need of a constantly expanding market for its products chases the bourgeoisie over the entire surface of the globe. It must nestle everywhere, settle everywhere, establish connections everywhere." -- Communist Manifesto From aba3600@REDACTED Wed Oct 18 19:09:32 2000 From: aba3600@REDACTED (Andy with Recycled Electrons) Date: Wed, 18 Oct 2000 12:09:32 -0500 (CDT) Subject: Algorithmic lists In-Reply-To: <200010180802.KAA19215@trana.bluetail.com> Message-ID: > >Finally, does anyone have an interst in seeing a link to a Masters Thesis > >written in Erlang? It deals with robotics. > > Yes, yes, yes, definitely! > I'll post the URL around Nov 1, when I defend. I hate to give links to works in progres. Sincerely, Andy Allen Recycled Electrons email: andy@REDACTED From compudata@REDACTED Thu Oct 19 01:04:11 2000 From: compudata@REDACTED (Compudata Systems Consulting) Date: Wed, 18 Oct 2000 17:04:11 -0600 Subject: Suggested Example/ emacs questions References: <200010170132.SAA13779@eme110-115.Sendmail.COM> Message-ID: <39EE2C6B.397154D9@cadvision.com> Jim Larson wrote: > Thing. Henry Baker's essay > > http://ftp.netcom.com/pub/hb/hbaker/TreeShadow.html > > (the link may not work - Netcom's site seems to have gone away - > anyone know where it's gone to these days?) makes the interesting Looking through deja.com, it seems netcom is dead and gone (eulogies, sorrowful posts about the final seconds, et cetera). Does anyone know of anyplace else this might be archived? -- Christopher Clark You know, the language of some of those messages that come up, it just absolutely drives me crazy. They are flat-out incomprehensible. -- William Gates II (BillG's father) on Windows error messages From johnnygood@REDACTED Sat Oct 14 06:23:02 2000 From: johnnygood@REDACTED (aa) Date: Sat, 14 Oct 2000 12:23:02 +0800 Subject: =?big5?B?p9a81lNPSE+x2g==?= Message-ID: <200010190023.e9J0NZ916118@hades.cslab.ericsson.net> ??????????????? ????????????SOHO?? ??????????,??????? ?????????,?????????,???????? ??????,???????????? ????????,??????????????? ???????????,???????? ??: (1)????? ?????????? ? (2)??????100??,??????? (3)???????????????????? (4)?????,???????????! (5)????????????? ? ?????, ?????,????!!????????????? ??????????, ????, ???? ! ???????+???????+????????=?????????? ?????????,?????........... ?? ?? ?? ??? Email ???Email????jojoone@REDACTED -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: human69.gif Type: image/gif Size: 5473 bytes Desc: not available URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: background121.gif Type: image/gif Size: 3485 bytes Desc: not available URL: From stimuli@REDACTED Thu Oct 19 03:11:26 2000 From: stimuli@REDACTED (Jeffrey Straszhiem) Date: Wed, 18 Oct 2000 21:11:26 -0400 Subject: Thoughts on laziness (long) In-Reply-To: <200010181111.NAA19866@trana.bluetail.com> References: <20001017194739.A1428@bzzt.shadow.net> <200010181111.NAA19866@trana.bluetail.com> Message-ID: <20001018211126.A1853@bzzt.shadow.net> On Wed, Oct 18, 2000 at 01:11:44PM +0200, Robert Virding wrote: > I personally think that laziness can be interesting but have yet > come across any applications in Erlang where it would have been > useful. > Many of the uses of laziness in other functional languages are handled > in Erlang with processes and message communication. Implementing lazy > streams for communication does not give you very much and in many cases > is much more limiting than Erlang's message passing with selective > receives. True, Erlang's rich process and message features do provide alternatives to lazy streams for many tasks. However, I think Okasaki's amortized data structures might alone provide a good reason for some call-by-need laziness features. > Another problem with call-by-need, which I tried to point out in an > earlier mail, is that it creates problems when used in conjunction > with processes and distribution. For many applicattions it IS > important WHEN and WHERE things are done. > You also have the proble that in Erlang programs there are many > functiona calls which are done for effect, send a message, so > waiting to evaluate them until you need the result won't give you > much. :-) True, but these are areas where one would surely *not* choose a lazy structure. Regarding whether messages should be forced, if you are only using laziness on side-effect-free values (as you should), it doesn't matter when the suspension is forced. And as suspensions are just memoized Funs, and you can pass Funs as messages, there certainly is an option to send the suspension unforced. That being said, you give good reasons to force them. I don't know which would be best, and this should be left an issue of further research (by smarter people than me). For the purposes of amortized structures, there are arguments that could be made for both options, and an even stronger argument for putting suspensions in shared heap -- but that is an entirely different topic I won't address :) > My solution was, as mentioned, force full evalutaion for all messages > and for all calls which ignore the result. This should make the most > things work, but you will still end up with sequencing problems: > R1 = foo(...), > R2 = bar(...), > If R1 and R2 are used then both foo nad bar will be called but I may > lose control of the order, which may be important. This is true, and without some Byzantine structure like monads, I don't see an easy solution. Perhaps laziness should be assigned to the "only use this if you know what you're doing" category (which is fortunately a small category in Erlang space, but needn't be an empty category). For instance, Okasaki's amortized queues wouldn't suffer because all values place into the queue will have been evaluated before insertion (as Erlang is still elsewhere strict). Only the internal queue structures (the cons's and such) themselves will be modified lazily. This would be safe and useful, and could be provided in a nice module. > However, I wonder if it possible to update Erlang with such a feature > and still keep it sufficiently backwards compatible to call be able to > call it the same language. You might end up with a new language, Lazy > Erlang, with new semantics. Sure it would be backward compatible, as laziness would only be a small addition to the language -- and only used just where specifically desired. I don't think anyone is advocating replacing Erlang's present computational model with a lazy one, nor replacing strict lists with lazy streams. Lazy-by-default languages are complex beasts, often hard to learn, and with a vague feeling of "specifications on an amorphous sea of computation". Erlang's simple and direct model is easy to understand, and easy to learn. This must never change. (As usual, just my 2 monetary units.) -- Jeffrey Straszheim | A sufficiently advanced -- Systems Engineer, Programmer | regular expression is -- http://www.shadow.net/~stimuli | indistinguishable from -- stimuli AT shadow DOT net | magic From bjorn@REDACTED Thu Oct 19 10:39:29 2000 From: bjorn@REDACTED (Bjorn Gustavsson) Date: 19 Oct 2000 10:39:29 +0200 Subject: Suggestion: New table iterators Message-ID: I suggest that we add ets:foldl(Fun, Acc, Table) ets:foldr(Fun, Acc, Table) dets:foldl(Fun, Acc, Table) dets:foldr(Fun, Acc, Table) mnesia:foldl(Fun, Acc, Table) mnesia:foldr(Fun, Acc, Table) for iteration over tables. The difference between the foldl/3 and foldr/3 is the order in which the table elements are accessed. foldl/3 accesses from the first element to the last, while foldr/3 accesses the element from last to to first. Both functions are tail-recursive. The order is important only for ordered tables. There is no need for map/2 or filter/3 functions, because they can easily be simulated with the fold functions: my_tab2list(T) -> ets:foldr(fun (E, Acc) -> [E|Acc] end, [], T). Besides, the reasonable semantics for map/2 and filter/3 would be that they modified the ets table or returned a new ets table, something we we are no prepared to implement now, but might be implemented in a future version. Any comments? /Bj?rn -- Bj?rn Gustavsson Ericsson Utvecklings AB bjorn@REDACTED ?T2/UAB/F/P BOX 1505 +46 8 727 56 87 125 25 ?lvsj? From etxuwig@REDACTED Thu Oct 19 10:49:14 2000 From: etxuwig@REDACTED (Ulf Wiger) Date: Thu, 19 Oct 2000 10:49:14 +0200 (MET DST) Subject: Suggestion: New table iterators In-Reply-To: Message-ID: Perhaps ets:last/1 on a set table could be modified to actually fetch the "last" object, and not the "first", as it does now? (On set tables, ets:last/1 -> ets:first/1. I assume this was done because order doesn't really make sense in a set table; even so, in order for e.g. ets:foldr/3 to be implemented with ets:last/1 + ets:prev/2, this would have to change.) Apart from that, I think it's a good idea. mnesia:fold[lr]/3 is a bit complicated, if e.g. we want to fold over an ordered_set table inside a transaction, after objects have been written to the table from within the same transaction. The new objects have not been written to the original table yet, but reside in a temporary table. Thus, the order is a bit more difficult to preserve. /Uffe On 19 Oct 2000, Bjorn Gustavsson wrote: >I suggest that we add > > ets:foldl(Fun, Acc, Table) > ets:foldr(Fun, Acc, Table) > > dets:foldl(Fun, Acc, Table) > dets:foldr(Fun, Acc, Table) > > mnesia:foldl(Fun, Acc, Table) > mnesia:foldr(Fun, Acc, Table) > >for iteration over tables. > >The difference between the foldl/3 and foldr/3 is the order in which the >table elements are accessed. foldl/3 accesses from the first element to >the last, while foldr/3 accesses the element from last to to first. >Both functions are tail-recursive. The order is important only for ordered tables. > >There is no need for map/2 or filter/3 functions, because they can easily >be simulated with the fold functions: > > my_tab2list(T) -> > ets:foldr(fun (E, Acc) -> [E|Acc] end, [], T). > >Besides, the reasonable semantics for map/2 and filter/3 would be that >they modified the ets table or returned a new ets table, something we >we are no prepared to implement now, but might be implemented in a future version. > >Any comments? > >/Bj?rn > -- Ulf Wiger tfn: +46 8 719 81 95 Strategic Product & System Management mob: +46 70 519 81 95 Ericsson Telecom AB, Datacom Networks and IP Services Varuv?gen 9, ?lvsj?, S-126 25 Stockholm, Sweden From Chandrashekhar.Mullaparthi@REDACTED Thu Oct 19 10:49:01 2000 From: Chandrashekhar.Mullaparthi@REDACTED (Chandrashekhar Mullaparthi) Date: Thu, 19 Oct 2000 09:49:01 +0100 Subject: Suggested Example/ emacs questions Message-ID: <402DD461F109D411977E0008C791C31202A796B1@imp02mbx.one2one.co.uk> Google has it cached. http://citeseer.nj.nec.com/cs?profile=1780456%2C250065%2C1%2C0.25%2CDownload &rd=http%3A//citeseer.nj.nec.com/papers/cs/11431/ftp%253A%2523@REDACTED@%2523%2523@ S@%2523ftp.netcom.com%2523@REDACTED@%2523pub%2523@REDACTED@%2523hb%2523@REDACTED@%2523hbaker%2523 @S@%2523TreeShadow.pdf -----Original Message----- Jim Larson wrote: > Thing. Henry Baker's essay > > http://ftp.netcom.com/pub/hb/hbaker/TreeShadow.html > > (the link may not work - Netcom's site seems to have gone away - > anyone know where it's gone to these days?) makes the interesting Looking through deja.com, it seems netcom is dead and gone (eulogies, sorrowful posts about the final seconds, et cetera). Does anyone know of anyplace else this might be archived? NOTICE AND DISCLAIMER: This email (including attachments) is confidential. If you have received this email in error please notify the sender immediately and delete this email from your system without copying or disseminating it or placing any reliance upon its contents. We cannot accept liability for any breaches of confidence arising through use of email. Any opinions expressed in this email (including attachments) are those of the author and do not necessarily reflect our opinions. We will not accept responsibility for any commitments made by our employees outside the scope of our business. We do not warrant the accuracy or completeness of such information. From rv@REDACTED Thu Oct 19 10:51:58 2000 From: rv@REDACTED (Robert Virding) Date: Thu, 19 Oct 2000 10:51:58 +0200 Subject: Suggestion: New table iterators In-Reply-To: Your message of "19 Oct 2000 10:39:29 +0200." Message-ID: <200010190851.KAA23757@trana.bluetail.com> Bjorn Gustavsson writes: >I suggest that we add > > ets:foldl(Fun, Acc, Table) > ets:foldr(Fun, Acc, Table) > > dets:foldl(Fun, Acc, Table) > dets:foldr(Fun, Acc, Table) > > mnesia:foldl(Fun, Acc, Table) > mnesia:foldr(Fun, Acc, Table) > >for iteration over tables. > >The difference between the foldl/3 and foldr/3 is the order in which the >table elements are accessed. foldl/3 accesses from the first element to >the last, while foldr/3 accesses the element from last to to first. >Both functions are tail-recursive. The order is important only for ordered tables. Definitely YES! But I am not really convinced you need l/r. Perhaps a just fold/3 as well? I generally think it is a good idea to add iterators over to make it easier and, more importantly, clearer what you are doing. >There is no need for map/2 or filter/3 functions, because they can easily >be simulated with the fold functions: > > my_tab2list(T) -> > ets:foldr(fun (E, Acc) -> [E|Acc] end, [], T). > >Besides, the reasonable semantics for map/2 and filter/3 would be that >they modified the ets table or returned a new ets table, something we >we are no prepared to implement now, but might be implemented in a future version. > >Any comments? I definitely think that map and filter should return a modified table to keep the general semantics of mapping and filtering consistent. Not returning a new table is OK here as the semantics of tables is destructive. With those semantics it would OK to add those functions now but with relatively naive implementations. At least filter. Robert -- Robert Virding Tel: +46 (0)8 545 55 017 Alteon Web Systems Email: rv@REDACTED S:t Eriksgatan 44 WWW: http://www.bluetail.com/~rv SE-112 34 Stockholm, SWEDEN "Folk s?ger att jag inte bryr mig om n?gonting, men det skiter jag i". From etxuwig@REDACTED Thu Oct 19 10:55:17 2000 From: etxuwig@REDACTED (Ulf Wiger) Date: Thu, 19 Oct 2000 10:55:17 +0200 (MET DST) Subject: Suggestion: New table iterators In-Reply-To: Message-ID: Also, it would be nice to perform a list comprehension on an ets table. Using my hack of sys_pre_expand, it would have been easily accomplished by just adding a function ets:tab2lazy_list(Tab) -> K = ets:first(Tab), [Obj] = ets:lookup(Tab, K), [K|fun() -> next_in_tab(Tab, ets:next(Tab, K)) end]. next_in_tab(Tab, '$end_of_table') -> []; next_in_tab(Tab, Key) -> [Obj] = ets:lookup(Tab, Key), [Obj | fun() -> next_in_tab(Tab, ets:next(Tab, K)) end]. (ignoring the complication of bag tables for demo purposes.) Oh well. I'm sure it's not that hard anyway. /Uffe On 19 Oct 2000, Bjorn Gustavsson wrote: >I suggest that we add > > ets:foldl(Fun, Acc, Table) > ets:foldr(Fun, Acc, Table) > > dets:foldl(Fun, Acc, Table) > dets:foldr(Fun, Acc, Table) > > mnesia:foldl(Fun, Acc, Table) > mnesia:foldr(Fun, Acc, Table) > >for iteration over tables. > >The difference between the foldl/3 and foldr/3 is the order in which the >table elements are accessed. foldl/3 accesses from the first element to >the last, while foldr/3 accesses the element from last to to first. >Both functions are tail-recursive. The order is important only for ordered tables. > >There is no need for map/2 or filter/3 functions, because they can easily >be simulated with the fold functions: > > my_tab2list(T) -> > ets:foldr(fun (E, Acc) -> [E|Acc] end, [], T). > >Besides, the reasonable semantics for map/2 and filter/3 would be that >they modified the ets table or returned a new ets table, something we >we are no prepared to implement now, but might be implemented in a future version. > >Any comments? > >/Bj?rn > -- Ulf Wiger tfn: +46 8 719 81 95 Strategic Product & System Management mob: +46 70 519 81 95 Ericsson Telecom AB, Datacom Networks and IP Services Varuv?gen 9, ?lvsj?, S-126 25 Stockholm, Sweden From jakob@REDACTED Thu Oct 19 11:01:04 2000 From: jakob@REDACTED (Jakob Cederlund =?iso-8859-1?Q?på?= UAB) Date: Thu, 19 Oct 2000 11:01:04 +0200 Subject: Suggestion: New table iterators In-Reply-To: Message-ID: <5.0.0.25.0.20001019104951.00b4ede0@mail> At 10:39 2000-10-19 +0200, Bjorn Gustavsson wrote: >I suggest that we add > > ets:foldl(Fun, Acc, Table) > ets:foldr(Fun, Acc, Table) ... Hmm... it's easily done, but is it the right way? What you want is a way to iterate over containers, that is not tied to the implementation of the container. The way of iterating in erlang, recursing with [Car | Cdr] is tied to the list type. Lazy evaluation, with language support, could be one way of replacing the | Cdr with a fun-call, without having to rewrite a lot of code. Using foldl or foldr instead of [Car | Cdr] is another way of doing it, perhaps yielding less readable code. But it's still not easy to change underlying representation. And it doesn't solve the initial problem, that there is no easy way for isolating the algorithms on containers from the implementation of the container. (Call it OOP or generic programming or whatever.) /Jakob From etxuwig@REDACTED Thu Oct 19 11:17:07 2000 From: etxuwig@REDACTED (Ulf Wiger) Date: Thu, 19 Oct 2000 11:17:07 +0200 (MET DST) Subject: Suggestion: New table iterators In-Reply-To: <5.0.0.25.0.20001019104951.00b4ede0@mail> Message-ID: On Thu, 19 Oct 2000, Jakob Cederlund p? UAB wrote: >At 10:39 2000-10-19 +0200, Bjorn Gustavsson wrote: >>I suggest that we add >> >> ets:foldl(Fun, Acc, Table) >> ets:foldr(Fun, Acc, Table) >... > >Hmm... it's easily done, but is it the right way? > >What you want is a way to iterate over containers, that is not tied to the >implementation of the container. > >The way of iterating in erlang, recursing with [Car | Cdr] is tied >to the list type. Lazy evaluation, with language support, could be >one way of replacing the | Cdr with a fun-call, without having to >rewrite a lot of code. This is true. After shying away a bit from the discussion on Lazy evaluation, I've decided to punt, and try to get support for the common list iterator functions (including list comprehensions) over ets and mnesia tables. The standard [H|T] syntax can be augmented through the [H|fun()] trick - which seems to be frowned upon(?), but not forbidden - leaving most of the code intact. We will probably use that (with care) in places where it solves the problem nicely. >Using foldl or foldr instead of [Car | Cdr] is another way of doing >it, perhaps yielding less readable code. But it's still not easy to >change underlying representation. > >And it doesn't solve the initial problem, that there is no easy way >for isolating the algorithms on containers from the implementation >of the container. (Call it OOP or generic programming or whatever.) > >/Jakob Personally, I still think the [H|fun()] trick is the best candidate for solving that quickly, but I'm fully prepared to let that go if it confuses things or risks being a dead end in a longer perspective. /Uffe -- Ulf Wiger tfn: +46 8 719 81 95 Strategic Product & System Management mob: +46 70 519 81 95 Ericsson Telecom AB, Datacom Networks and IP Services Varuv?gen 9, ?lvsj?, S-126 25 Stockholm, Sweden From rv@REDACTED Thu Oct 19 11:27:47 2000 From: rv@REDACTED (Robert Virding) Date: Thu, 19 Oct 2000 11:27:47 +0200 Subject: Suggestion: New table iterators In-Reply-To: Your message of "Thu, 19 Oct 2000 10:55:17 +0200." Message-ID: <200010190927.LAA23873@trana.bluetail.com> Ulf Wiger writes: > >Also, it would be nice to perform a list comprehension on an ets >table. > >Using my hack of sys_pre_expand, it would have been easily >accomplished by just adding a function > >ets:tab2lazy_list(Tab) -> > K = ets:first(Tab), > [Obj] = ets:lookup(Tab, K), > [K|fun() -> next_in_tab(Tab, ets:next(Tab, K)) end]. > >next_in_tab(Tab, '$end_of_table') -> []; >next_in_tab(Tab, Key) -> > [Obj] = ets:lookup(Tab, Key), > [Obj | fun() -> next_in_tab(Tab, ets:next(Tab, K)) end]. > >(ignoring the complication of bag tables for demo purposes.) This is relatively easy to do in sys_pre_expand. On idea I had is to use list comprehensions with special forms for generators, for example R <- ets(foo) R <- mnesia(foo) which would extract elements from ets/mnesia tables. The implementation would be very similar to what you have written and generate the table elements one at a time and pass them into the lc. It would of course be possible to mix tabel and list generators. I prefer this way to generating lazy lists. But it is harder to pass an accumulator along. The problem with using lc is that you are explicitly building a list of values, you are going Table -> List. This abuses the lc notion, slightly. I think the fold function is MUCH better as it can do all the LC can, plus much more. The intent is also clearer. Robert -- Robert Virding Tel: +46 (0)8 545 55 017 Alteon Web Systems Email: rv@REDACTED S:t Eriksgatan 44 WWW: http://www.bluetail.com/~rv SE-112 34 Stockholm, SWEDEN "Folk s?ger att jag inte bryr mig om n?gonting, men det skiter jag i". From bjorn@REDACTED Thu Oct 19 11:29:47 2000 From: bjorn@REDACTED (Bjorn Gustavsson) Date: 19 Oct 2000 11:29:47 +0200 Subject: Suggestion: New table iterators In-Reply-To: Robert Virding's message of "Thu, 19 Oct 2000 10:51:58 +0200" References: <200010190851.KAA23757@trana.bluetail.com> Message-ID: Robert Virding writes: > Bjorn Gustavsson writes: > >I suggest that we add > > > > ets:foldl(Fun, Acc, Table) > > ets:foldr(Fun, Acc, Table) > > > > dets:foldl(Fun, Acc, Table) > > dets:foldr(Fun, Acc, Table) > > > > mnesia:foldl(Fun, Acc, Table) > > mnesia:foldr(Fun, Acc, Table) > > > >for iteration over tables. > > > >The difference between the foldl/3 and foldr/3 is the order in which the > >table elements are accessed. foldl/3 accesses from the first element to > >the last, while foldr/3 accesses the element from last to to first. > >Both functions are tail-recursive. The order is important only for ordered tables. > > Definitely YES! But I am not really convinced you need l/r. Perhaps a > just fold/3 as well? I generally think it is a good idea to add > iterators over to make it easier and, more importantly, clearer what > you are doing. Ok, maybe order isn't that important. It would definitely be simpler and clearer having just ets:fold(Fun, Acc, Table) dets:fold(Fun, Acc, Table) mnesia:fold(Fun, Acc, Table) It would also avoid some of the ordering problems Ulf mentioned in his mail (bug in ets:last/1, Mnesia transactions). Is anyone against implmentering fold/3 instead of fold[rl]/3? > > >There is no need for map/2 or filter/3 functions, because they can easily > >be simulated with the fold functions: > > > > my_tab2list(T) -> > > ets:foldr(fun (E, Acc) -> [E|Acc] end, [], T). > > > >Besides, the reasonable semantics for map/2 and filter/3 would be that > >they modified the ets table or returned a new ets table, something we > >we are no prepared to implement now, but might be implemented in a future version. > > > >Any comments? > > I definitely think that map and filter should return a modified table to > keep the general semantics of mapping and filtering consistent. Not > returning a new table is OK here as the semantics of tables is > destructive. With those semantics it would OK to add those functions > now but with relatively naive implementations. At least filter. > > Robert > > -- > Robert Virding Tel: +46 (0)8 545 55 017 > Alteon Web Systems Email: rv@REDACTED > S:t Eriksgatan 44 WWW: http://www.bluetail.com/~rv > SE-112 34 Stockholm, SWEDEN > "Folk s?ger att jag inte bryr mig om n?gonting, men det skiter jag i". > > -- Bj?rn Gustavsson Ericsson Utvecklings AB bjorn@REDACTED ?T2/UAB/F/P BOX 1505 +46 8 727 56 87 125 25 ?lvsj? From rv@REDACTED Thu Oct 19 11:36:48 2000 From: rv@REDACTED (Robert Virding) Date: Thu, 19 Oct 2000 11:36:48 +0200 Subject: Suggestion: New table iterators In-Reply-To: Your message of "Thu, 19 Oct 2000 11:01:04 +0200." <5.0.0.25.0.20001019104951.00b4ede0@mail> Message-ID: <200010190936.LAA23925@trana.bluetail.com> Jakob Cederlund =?iso-8859-1?Q?p??= UAB writes: >At 10:39 2000-10-19 +0200, Bjorn Gustavsson wrote: >>I suggest that we add >> >> ets:foldl(Fun, Acc, Table) >> ets:foldr(Fun, Acc, Table) >... > >Hmm... it's easily done, but is it the right way? > >... >And it doesn't solve the initial problem, that there is no easy way for >isolating the algorithms on containers from the implementation of the >container. (Call it OOP or generic programming or whatever.) That, fortunately or unfortunately, is the way it is with Erlang as it stands today. Seeing there is no static typing there is no way for a compiler to work out which function it should call, you have to tell the system that. Anyway, you usually know what it is. I don't think the generics are that useful, but give me an example if I am wrong. What you have abstracted out IS the basic algorithm on the containers, map/fold/filter whatever, and hidden the underlying implementation. you can fold over a list/set/ets table/mnesia table without knowing the low-level details of how to do it. But you still have to know WHAT it is. Robert -- Robert Virding Tel: +46 (0)8 545 55 017 Alteon Web Systems Email: rv@REDACTED S:t Eriksgatan 44 WWW: http://www.bluetail.com/~rv SE-112 34 Stockholm, SWEDEN "Folk s?ger att jag inte bryr mig om n?gonting, men det skiter jag i". From etxuwig@REDACTED Thu Oct 19 11:39:28 2000 From: etxuwig@REDACTED (Ulf Wiger) Date: Thu, 19 Oct 2000 11:39:28 +0200 (MET DST) Subject: Suggestion: New table iterators In-Reply-To: <200010190927.LAA23873@trana.bluetail.com> Message-ID: On Thu, 19 Oct 2000, Robert Virding wrote: >The problem with using lc is that you are explicitly building a list >of values, you are going Table -> List. This abuses the lc notion, >slightly. But a list comprehension over ets is not that much different from a mnemosyne query on mnesia. Mnemosyne also goes Table -> List. An option would be to extend mnemosyne (not standard LC) to also operate on ets tables. This brings us into the area of "dirty mnemosyne", which we at AXD 301 have suggested in the past - and then had to duck for cover. ;) >I think the fold function is MUCH better as it can do all the LC >can, plus much more. The intent is also clearer. Is this a general critique against LC? (: Returning to (my) original problem of changing data storage options with minimal impact to the code, it should be possible to use LCs over tables (ets or mnesia) as well as over lists. The same goes with the high-level iterator functions in lists. Regarding LC vs. fold functions, I've noticed that some people seem to much prefer one or the other. Some love list comprehensions and hate foldl; others enjoy the lists functions, but don't like LCs; yet others hate them both and want to write their own iterations using list syntax. Personally, I love them all equally. (: /Uffe -- Ulf Wiger tfn: +46 8 719 81 95 Strategic Product & System Management mob: +46 70 519 81 95 Ericsson Telecom AB, Datacom Networks and IP Services Varuv?gen 9, ?lvsj?, S-126 25 Stockholm, Sweden From matthias@REDACTED Thu Oct 19 12:17:42 2000 From: matthias@REDACTED (matthias@REDACTED) Date: Thu, 19 Oct 2000 12:17:42 +0200 (CEST) Subject: getting Erlang to run on an embedded powerpc 860 Message-ID: <14830.51782.915594.972691@corelatus.com> Hi, I now have Erlang running on an embedded powerpc board, but the way I got it running seems "somewhat unsatisfying", maybe people here have some better ideas. The board: Motorola PPC860 CPU Runs a cut-down linux Does *not* have a native development environment, e.g. no 'make', no 'perl', no gcc. The PPC860 is "almost" compatible with the PowerPC CPUs found in macintosh machines. In practice, binaries compiled for, e.g. a PPC603 will crash on the 860. So I have to cross compile. Approach #1: Try to understand autoconf. Modify the configure stuff so that it makes makefiles suitable for cross compilation. After a frustrating day fighting with this I decided to leave playing with autoconf for braver people. Approach #2: Run configure on my x86 machine, and then let some scripts run through the source tree and 'fix' the Makefiles. In more detail: ./configure find . -name Makefile -exec mung \{\} \; vi make/i686-pc-linux-gnu/otp.mk vi erts/emulator/i686-pc-linux-gnu/Makefile make noboot fiddle with some -L paths which caused make to fail make noboot copy over the library .beams from my x86 version done 'mung' is a script which runs the makefiles through a few sed filters which change all occurences of CC = gcc to CC = powerpc-linux-gcc and do similar things to LD, AR, RANLIB and so on. This approach seems unsatisfying for two reasons. Firstly 'configure' might have set some CPU-dependent flag, e.g. x86 is little endian and PPC is big endian. Since the resulting Erlang "seems to work ok" on the 860 I'll just assume that's not the case. Secondly it feels like a gigantic hack, though seeing that Patrik's approach to compiling for VxWorks isn't much more sophisticated made me feel better ;-) Comments? Suggestions? Matthias From Sean.Hinde@REDACTED Thu Oct 19 12:54:55 2000 From: Sean.Hinde@REDACTED (Sean Hinde) Date: Thu, 19 Oct 2000 11:54:55 +0100 Subject: Suggestion: New table iterators Message-ID: <402DD461F109D411977E0008C791C31256522A@imp02mbx.one2one.co.uk> I'd like to add my support for iterators over ets/dets/mnesia. At the moment writing a safe iterator over an mnesia table is not obvious if you want to also have concurrent updates. Apart from anything else there appears to be no guarantee that a table will not be re-hashed during the iteration - even in a transaction. If you want to delete stuff during the iteration it is even less obvious how to do this safely and simply. Building a list of stuff to be deleted later can chew up masses of memory.. Sean NOTICE AND DISCLAIMER: This email (including attachments) is confidential. If you have received this email in error please notify the sender immediately and delete this email from your system without copying or disseminating it or placing any reliance upon its contents. We cannot accept liability for any breaches of confidence arising through use of email. Any opinions expressed in this email (including attachments) are those of the author and do not necessarily reflect our opinions. We will not accept responsibility for any commitments made by our employees outside the scope of our business. We do not warrant the accuracy or completeness of such information. From bjorn@REDACTED Thu Oct 19 14:03:37 2000 From: bjorn@REDACTED (Bjorn Gustavsson) Date: 19 Oct 2000 14:03:37 +0200 Subject: Suggestion: New table iterators In-Reply-To: Sean Hinde's message of "Thu, 19 Oct 2000 11:54:55 +0100" References: <402DD461F109D411977E0008C791C31256522A@imp02mbx.one2one.co.uk> Message-ID: In the first version of the fold function, which we plan to release as a patch for R7B, we will just encapsulate existing functionality in an iterator. We will use safe_fixtable / fixtable, but we will not add any new locking mechanims to ets or dets. /Bj?rn Sean Hinde writes: > I'd like to add my support for iterators over ets/dets/mnesia. > > At the moment writing a safe iterator over an mnesia table is not obvious if > you want to also have concurrent updates. Apart from anything else there > appears to be no guarantee that a table will not be re-hashed during the > iteration - even in a transaction. > > If you want to delete stuff during the iteration it is even less obvious how > to do this safely and simply. Building a list of stuff to be deleted later > can chew up masses of memory.. > > Sean > > > > NOTICE AND DISCLAIMER: > This email (including attachments) is confidential. If you have received > this email in error please notify the sender immediately and delete this > email from your system without copying or disseminating it or placing any > reliance upon its contents. We cannot accept liability for any breaches of > confidence arising through use of email. Any opinions expressed in this > email (including attachments) are those of the author and do not necessarily > reflect our opinions. We will not accept responsibility for any commitments > made by our employees outside the scope of our business. We do not warrant > the accuracy or completeness of such information. > > -- Bj?rn Gustavsson Ericsson Utvecklings AB bjorn@REDACTED ?T2/UAB/F/P BOX 1505 +46 8 727 56 87 125 25 ?lvsj? From hakan@REDACTED Thu Oct 19 14:04:23 2000 From: hakan@REDACTED (Hakan Mattsson) Date: Thu, 19 Oct 2000 14:04:23 +0200 (MET DST) Subject: Suggestion: New table iterators In-Reply-To: <402DD461F109D411977E0008C791C31256522A@imp02mbx.one2one.co.uk> Message-ID: On Thu, 19 Oct 2000, Sean Hinde wrote: Sean> I'd like to add my support for iterators over ets/dets/mnesia. Yes, at least they looks cute. But in order to enable a real efficient implementation of them, the not yet released select functionality in ets and dets is needed. Then it would be possible to efficiently perform parts of the filtering, locally, near the data source, avoiding lots of copying. With interruptable semantics like ets:db_match_object/3, extended with the possibility to access the interim result in each iteration chunk it would really open up for blinding fast Mnesia iterators. This could of course be combined with cute list-like iterators, where a fun only is applied on those records that fulfills the first select constraints. In order to hide the efficient low level select constraints it would be possible to let the Mnemosyne compiler generate them from its nice list comprehension syntax. Sean> At the moment writing a safe iterator over an mnesia table is not obvious if Sean> you want to also have concurrent updates. Apart from anything else there Sean> appears to be no guarantee that a table will not be re-hashed during the Sean> iteration - even in a transaction. Sean> Sean> If you want to delete stuff during the iteration it is even less obvious how Sean> to do this safely and simply. Building a list of stuff to be deleted later Sean> can chew up masses of memory.. The trouble with dynamic rehashing of Mnesia tables is only a problem if you perform dirty updates during the iteration. You may safely iterate over a table in a transaction iff no other process concurrently performs dirty updates of the same table. Inside the transaction you must first grab a lock on the entire table, before you start iterating over the table with mnesia:dirty_first and mnesia:dirty_next. The key lookups may safely be performed with mnesia:read or mnesia:dirty_read. If the mnesia:read does not return any records you may happily continue with the next key, since it means that you have deleted the record inside the enclosing transaction. Do NOT use the mnesia:dirty* functions to perform updates during the iteration. Use the transaction protected equivalents functions instead. /H?kan From etxuwig@REDACTED Thu Oct 19 14:16:09 2000 From: etxuwig@REDACTED (Ulf Wiger) Date: Thu, 19 Oct 2000 14:16:09 +0200 (MET DST) Subject: Suggestion: New table iterators In-Reply-To: Message-ID: On Thu, 19 Oct 2000, Hakan Mattsson wrote: >On Thu, 19 Oct 2000, Sean Hinde wrote: > >Sean> I'd like to add my support for iterators over ets/dets/mnesia. > >Yes, at least they looks cute. But in order to enable a real efficient >implementation of them, the not yet released select functionality in >ets and dets is needed. Then it would be possible to efficiently >perform parts of the filtering, locally, near the data source, >avoiding lots of copying. Yes, I've toyed with ets:select/2 - it's pretty powerful, but the syntax leaves a little something to be desired (looks like Lisp's ugly cousin.) The same goes for the trace functionality: my first impression is that it's great, but much too difficult to specify the trace filters, since they're not expressed in "standard Erlang". The documentation is worse though (may have something to do with the fact that it's not yet released... (: ). You have to read the docs on trace filters and/or the code in erl_db*.c, and/or use trial and error. If anyone wants to peek at it, ets:select(Tab, Pattern) is the function to use. Especially in combination with ordered sets, you can perform really fast queries with reasonably complex select logic. I understand fully that it's not ready, not released, not supported, and all that, but hey - that's where the fun lies, right? /Uffe -- Ulf Wiger tfn: +46 8 719 81 95 Strategic Product & System Management mob: +46 70 519 81 95 Ericsson Telecom AB, Datacom Networks and IP Services Varuv?gen 9, ?lvsj?, S-126 25 Stockholm, Sweden From rv@REDACTED Thu Oct 19 14:51:53 2000 From: rv@REDACTED (Robert Virding) Date: Thu, 19 Oct 2000 14:51:53 +0200 Subject: Suggestion: New table iterators In-Reply-To: Your message of "19 Oct 2000 14:03:37 +0200." Message-ID: <200010191251.OAA24545@trana.bluetail.com> Bjorn Gustavsson writes: >In the first version of the fold function, which we plan to release as >a patch for R7B, we will just encapsulate existing functionality in an >iterator. We will use safe_fixtable / fixtable, but we will not add any new >locking mechanims to ets or dets. Question is whether it would not be better to let the caller handle that and do fixtable themselves if they feel they need it. What about if the iterator fails? Robert -- Robert Virding Tel: +46 (0)8 545 55 017 Alteon Web Systems Email: rv@REDACTED S:t Eriksgatan 44 WWW: http://www.bluetail.com/~rv SE-112 34 Stockholm, SWEDEN "Folk s?ger att jag inte bryr mig om n?gonting, men det skiter jag i". From bjornfot@REDACTED Thu Oct 19 11:34:37 2000 From: bjornfot@REDACTED (Lars Bjornfot) Date: Thu, 19 Oct 2000 11:34:37 +0200 Subject: getting Erlang to run on an embedded powerpc 860 References: <14830.51782.915594.972691@corelatus.com> Message-ID: <39EEC02D.2F74B103@erix.ericsson.se> > which change all occurences of CC = gcc to CC = powerpc-linux-gcc and > do similar things to LD, AR, RANLIB and so on. This you can get with environment variable GCC_EXEC_PREFIX. Set to "$path/powerpc-linux-". Lasse From etxuwig@REDACTED Thu Oct 19 15:04:42 2000 From: etxuwig@REDACTED (Ulf Wiger) Date: Thu, 19 Oct 2000 15:04:42 +0200 (MET DST) Subject: job control with OTP R7B Message-ID: I've noticed that if I hit Ctrl-Z (using tcsh) to suspend Erlang, I'm having trouble getting OTP R7B back with 'fg'. > fg erl Suspended > jobs -l [1] - 9347 Running ... [3] 9721 Running ... [4] + 9811 Suspended erl This doesn't happen with OTP R5 and R6. They happily come back when I hit 'fg'. /Uffe -- Ulf Wiger tfn: +46 8 719 81 95 Strategic Product & System Management mob: +46 70 519 81 95 Ericsson Telecom AB, Datacom Networks and IP Services Varuv?gen 9, ?lvsj?, S-126 25 Stockholm, Sweden From bjorn@REDACTED Thu Oct 19 15:24:45 2000 From: bjorn@REDACTED (Bjorn Gustavsson) Date: 19 Oct 2000 15:24:45 +0200 Subject: Suggestion: New table iterators In-Reply-To: Robert Virding's message of "Thu, 19 Oct 2000 14:51:53 +0200" References: <200010191251.OAA24545@trana.bluetail.com> Message-ID: Robert Virding writes: > Bjorn Gustavsson writes: > >In the first version of the fold function, which we plan to release as > >a patch for R7B, we will just encapsulate existing functionality in an > >iterator. We will use safe_fixtable / fixtable, but we will not add any new > >locking mechanims to ets or dets. > > Question is whether it would not be better to let the caller handle > that and do fixtable themselves if they feel they need it. What about > if the iterator fails? I planned to catch any exception from the fun, unfix the table and rethrow the exception. I think that the whole point about the iterator is to hide unpleasant details about ets/dets. Therefore, I my opinion, fixing and unfixing tables should definitely be part of the iterator. /Bj?rn -- Bj?rn Gustavsson Ericsson Utvecklings AB bjorn@REDACTED ?T2/UAB/F/P BOX 1505 +46 8 727 56 87 125 25 ?lvsj? From matthias@REDACTED Thu Oct 19 15:33:55 2000 From: matthias@REDACTED (matthias@REDACTED) Date: Thu, 19 Oct 2000 15:33:55 +0200 (CEST) Subject: Thoughts on laziness (long) In-Reply-To: <20001018211126.A1853@bzzt.shadow.net> References: <20001017194739.A1428@bzzt.shadow.net> <200010181111.NAA19866@trana.bluetail.com> <20001018211126.A1853@bzzt.shadow.net> Message-ID: <14830.63555.45918.561264@corelatus.com> > Robert Virding wrote: > > I [...] have [not] yet come across any applications in Erlang > > where it [laziness] would have been useful. Jeremy Strazhiem wrote: > However, I think Okasaki's amortized data structures might alone > provide a good reason for some call-by-need laziness features. Writing data structures in functional languages leads to embarassment: almost no matter what you do, it's usually slower and often also has worse big-O behaviour than the equivalent in a language which allows destructive update. One solution is to come up with elegant but complicated data structures such as Okasaki's. Many of the more attractive ones require lazy semantics. If you go down that road, you can brag on comp.lang.functional that your functional language now also supports monadical-recursive-slowdown-higher-order-transmogrification. You also scare off many potential users. Another solution is to just provide hashtables a la ETS. Nowhere near as elegant, but (a) it doesn't require turning the language's semantics upside down and (b) it's reasonable to assume it's faster*. Matthias ------------------------------ * Every attempt I've seen to build a clever, general data structure in Erlang has resulted in something that's slower than either lists or ETS. Two examples: ra_list ra_list is one of Okasaki's data structures. It's a data structure with both list-like and array-like behaviour; you get O(log N) access to any element AND O(1) head()/tail() gb_trees gb_trees is on the user contributions page at erlang.org. As far as I can tell, it's supposed to have O(log N) access to elements. The documentation makes no claims beyond "highly efficient". Doing a quick benchmark doesn't give very encouraging results. Sum: take a list of numbers 1..N and sum them (not Gauss' way ;-) Update: change the 3rd, middle and 3rd-last elements in a list -----list------ ------ra-list-- -------ets----- gb_tree N sum update sum update sum update update ---------------------------------------------------------------------- 100 107 197 962 84 1660 50 87 1000 921 3666 9903 127 16k 60 89 10k 9324 46k 93k 168 168k 74 96 100k 493k 660k 1357k 188 2109k 84 97 The table shows execution time, i.e. larger numbers are worse. I can't see why anyone would want to use a gb_tree---ETS is faster at the very operation gb_tree is supposed to be really good at. The picture for ra_list is a little more complex, but it doesn't make me want to jump up and down saying "yay! ra-lists". Reasons why the above tests might be complete crap: - I might have implemented ra-list really badly - The above benchmark might be contrived specially to make ETS look good. Then again, with such small tuples, I think it's a worst-case for ETS. - I might just be showing that the current Erlang implementation sucks. Maybe ETOS or HIPE can show much better results for purely functional data structures. - I wasn't super-careful with the timing, e.g. it's all the same Erlang machine, so garbage collection from earlier test runs might have perturbed the results. Source code is available on request if someone wants to investigate this some more. --- eot --- From richardc@REDACTED Thu Oct 19 16:02:53 2000 From: richardc@REDACTED (Richard Carlsson) Date: Thu, 19 Oct 2000 16:02:53 +0200 (MET DST) Subject: Thoughts on laziness (long) In-Reply-To: <14830.63555.45918.561264@corelatus.com> Message-ID: On Thu, 19 Oct 2000 matthias@REDACTED wrote: > Doing a quick benchmark doesn't give very encouraging results. > > Sum: take a list of numbers 1..N and sum them (not Gauss' way ;-) > > Update: change the 3rd, middle and 3rd-last elements in a list > > -----list------ ------ra-list-- -------ets----- gb_tree > N sum update sum update sum update update > ---------------------------------------------------------------------- > 100 107 197 962 84 1660 50 87 > 1000 921 3666 9903 127 16k 60 89 > 10k 9324 46k 93k 168 168k 74 96 > 100k 493k 660k 1357k 188 2109k 84 97 > > The table shows execution time, i.e. larger numbers are worse. > I can't see why anyone would want to use a gb_tree---ETS is > faster at the very operation gb_tree is supposed to be really > good at. The picture for ra_list is a little more complex, but > it doesn't make me want to jump up and down saying "yay! ra-lists". Well, you couldn't expect to beat a destructive update with a tree rewrite (but actually, I'm surprised that the ETS tables were not even faster). Advantages of gb-trees are that they are much more lightweight (an empty tree is a tuple {0, nil}, so you can create and throw away as many as you like on the fly, and that they do no copying, since they reside on the local heap - you could try the same test inserting/updating/fetching some bigger things than integers and see the difference. Myself, I mainly use them for representing environments in program analysis or interpretation. > - I might just be showing that the current Erlang implementation > sucks. Maybe ETOS or HIPE can show much better results for > purely functional data structures. They should perform better in native code compared to ETS than under the BEAM emulator, but in general the complexity of the algorithms would remain the same. Richard Carlsson (richardc@REDACTED) (This space intentionally left blank.) E-mail: Richard.Carlsson@REDACTED WWW: http://www.csd.uu.se/~richardc/ From etxuwig@REDACTED Thu Oct 19 16:18:59 2000 From: etxuwig@REDACTED (Ulf Wiger) Date: Thu, 19 Oct 2000 16:18:59 +0200 (MET DST) Subject: Thoughts on laziness (long) In-Reply-To: <14830.63555.45918.561264@corelatus.com> Message-ID: On Thu, 19 Oct 2000 matthias@REDACTED wrote: >* Every attempt I've seen to build a clever, general data structure in > Erlang has resulted in something that's slower than either lists or > ETS. Two examples: I suspect that it's hard to beat plain lists on the "sum" benchmark, but then again, lists suck badly on the "update" benchmark, as does ets on "sum". So the niche of alternative structures would be to strike a good compromise between the two. >Reasons why the above tests might be complete crap: > > - The above benchmark might be contrived specially to make > ETS look good. Then again, with such small tuples, I think > it's a worst-case for ETS. Actually, I think it's the other way around. In my experience, having built a linear hashing table in Erlang, for example, ets scales better with an increasing number of objects, but loses to a heap-based implementation when the objects get large. For small objects, it's very difficult to beat ets, but with > 1KBytes/object, you stand a good chance. /Uffe -- Ulf Wiger tfn: +46 8 719 81 95 Strategic Product & System Management mob: +46 70 519 81 95 Ericsson Telecom AB, Datacom Networks and IP Services Varuv?gen 9, ?lvsj?, S-126 25 Stockholm, Sweden From bernardp@REDACTED Thu Oct 19 16:16:19 2000 From: bernardp@REDACTED (Pierpaolo BERNARDI) Date: Thu, 19 Oct 2000 16:16:19 +0200 (MET DST) Subject: Thoughts on laziness (long) In-Reply-To: <14830.63555.45918.561264@corelatus.com> Message-ID: On Thu, 19 Oct 2000 matthias@REDACTED wrote: > * Every attempt I've seen to build a clever, general data structure in > Erlang has resulted in something that's slower than either lists or > ETS. Two examples: > > ra_list > > ra_list is one of Okasaki's data structures. It's a data > structure with both list-like and array-like behaviour; > you get O(log N) access to any element AND O(1) head()/tail() And O(log N) replace. > Doing a quick benchmark doesn't give very encouraging results. > > Sum: take a list of numbers 1..N and sum them (not Gauss' way ;-) > > Update: change the 3rd, middle and 3rd-last elements in a list > > -----list------ ------ra-list-- -------ets----- gb_tree > N sum update sum update sum update update > ---------------------------------------------------------------------- > 100 107 197 962 84 1660 50 87 > 1000 921 3666 9903 127 16k 60 89 > 10k 9324 46k 93k 168 168k 74 96 > 100k 493k 660k 1357k 188 2109k 84 97 > > The table shows execution time, i.e. larger numbers are worse. I too implemented ralists. If I remember correctly, my implementation was about 5/6 times slower than built-in lists in a cons/head/tail-based benchmark. Source code is appended below. Worth noting is the fact that uninitialized RALs can be built which occupy very little space. E.g. ral:create(1000000000000) does work. PB ================================================================ %%% File : ral.erl %%% Author : Pierpaolo Bernardi %%% Purpose : Implements Random Access Lists. %%% Created : 14 Dec 1998 by Pierpaolo Bernardi % See Okasaki - Purely Functional Data Structures. -module(ral). -author('bernardp@REDACTED'). %-compile(export_all). -export([empty/0, cons/2, head/1, tail/1, is_empty/1, nth/2, update/3, ralength/1, create/2, create/1, drop/2, rev_append/2, rev/1, append/2, flatten/1, to_list/1, from_list/1, iota/1, map/2, iter/2, for_all/2, exists/2, mem/2, split/1, combine/2 ]). %% A RAL is represented either as the atom 'the_empty_ral', or a list %% of the form: [{Size,Tree} ... | the_empty_ral]. Size is the number %% of nodes contained in the Tree, Tree is either {X} for a leaf node %% or {X,LeftSubtree,RighSubtree} for nonleaf nodes. empty() -> the_empty_ral. % cons(Item,Ral). cons(X,[{Size1,T1}, {Size2,T2}|Rest]) when Size1 =:= Size2 -> [{1+Size1+Size2,{X,T1,T2}}|Rest]; cons(X,[{Size1,T1}, {Size2,T2}|Rest]) when Size1 /= Size2 -> [{1,{X}}|[{Size1,T1}, {Size2,T2}|Rest]]; cons(X,Y) -> [{1,{X}}|Y]. % head(Ral). head([{Size,{X}}|Rest]) -> X; head([{Size,{X,T1,T2}}|Rest]) -> X. % tail(Ral). tail([{Size,{X}}|Rest]) -> Rest; tail([{Size,{X,T1,T2}}|Rest]) -> Size1 = Size div 2, [{Size1,T1},{Size1,T2}|Rest]. % is_empty(Ral). is_empty(the_empty_ral) -> true; is_empty(X) -> false. tree_nth(Size,{X},0) -> X; tree_nth(Size,{X,T1,T2},0) -> X; tree_nth(Size,{X,T1,T2},I) -> Size1 = Size div 2, if I =< Size1 -> tree_nth(Size1,T1,I-1); true -> tree_nth(Size1,T2,I-1-Size1) end. % nth(Ral,Index). nth([{Size,T}|Rest],I) when I < Size -> tree_nth(Size,T,I); nth([{Size,T}|Rest],I) -> nth(Rest,I-Size). tree_update(Size,{X},0,Y) -> {Y}; tree_update(Size,{X,T1,T2},0,Y) -> {Y,T1,T2}; tree_update(Size,{X,T1,T2},I,Y) -> Size1 = Size div 2, if I =< Size1 -> {X,tree_update(Size1,T1,I-1,Y),T2}; true -> {X,T1,tree_update(Size1,T2,I-1-Size1,Y)} end. % update(Ral,Index,NewItem). update([{Size,T}|Rest],I,Y) when I < Size -> [{Size,tree_update(Size,T,I,Y)}|Rest]; update([{Size,T}|Rest],I,Y) -> [{Size,T}|update(Rest,I-Size,Y)]. % Additional efficient operations not described in paper (Okasaki) % ralength(Ral). ralength(the_empty_ral) -> 0; ralength([{Size,T}|Rest]) -> Size + ralength(Rest). make(X,N,Size,T,Rest) when Size > N -> Rest; make(X,N,Size,T,Rest) -> make(X, N, 1+Size+Size, {X,T,T}, [{Size,T}|Rest]). select(0,X,Xs) -> Xs; select(M,[{Size,T}|Rest],Xs) when M < Size -> select(M,Rest,Xs); select(M,[{Size,T}|Rest],Xs) -> select(M-Size,[{Size,T}|Rest],[{Size,T}|Xs]). % create(Size,Elem). create(N,X) when integer(N) -> % make a list of all trees up to size n, then select % those trees that form the greedy decomposition select(N,make(X,N,1,{X},the_empty_ral),the_empty_ral). create(N) -> create(N,not_initialized). tree_drop(Size,T,0,Rest) -> [{Size,T}|Rest]; tree_drop(Size,{X},1,Rest) -> Rest; tree_drop(Size,{X,T1,T2},I,Rest) -> Size1 = Size div 2, if I =< Size1 -> tree_drop(Size1,T1,I-1,[{Size1,T2}|Rest]); true -> tree_drop(Size1,T2,I-1-Size1,Rest) end. % drop(Ral,N). drop(Xs,0) -> Xs; drop([{Size,T}|Rest],I) -> if I < Size -> tree_drop(Size,T,I,Rest); true -> drop(Rest,I-Size) end. % Pierpaolo decompo_loop(C,Top) -> Next = C+C+1, if Next > Top -> {Top-C,[C]}; true -> {Rest,Sizes} = decompo_loop(Next,Top), if Next =:= Rest -> {0,[Next|Sizes]}; C > Rest -> {Rest,Sizes}; true -> {Rest-C,[C|Sizes]} end end. decompo(N) -> if N < 0 -> invalid_argument; N =:= 0 -> the_empty_ral; true -> {Rest,Sizes} = decompo_loop(1,N), case Rest of 0 -> Sizes; 1 -> [1|Sizes] end end. tree_rev_append({X},L) -> cons(X,L); tree_rev_append({X,T1,T2},L) -> tree_rev_append(T2, tree_rev_append(T1,cons(X,L))). % rev_append(Rovescianda,Tail). rev_append(the_empty_ral,L2) -> L2; rev_append([{Size,T1}|T2],L2) -> rev_append(T2, tree_rev_append(T1,L2)). % rev(Ral). rev(L) -> rev_append(L,the_empty_ral). tree_append({X},L) -> cons(X,L); tree_append({X,T1,T2},L) -> cons(X,tree_append(T1, tree_append(T2,L))). % append(Ral1,Ral2). append(the_empty_ral,L2) -> L2; append([{Size,T1}|T2],L2) -> tree_append(T1,append(T2,L2)). % flatten(RalOfRals). flatten(the_empty_ral) -> the_empty_ral; flatten(L) -> append(head(L),flatten(tail(L))). tree_to_list({X},Acc) -> [X|Acc]; tree_to_list({X,T1,T2},Acc) -> [X|tree_to_list(T1,tree_to_list(T2,Acc))]. tol(the_empty_ral,Acc) -> Acc; tol([{Size,T1}|T2],Acc) -> tree_to_list(T1,tol(T2,Acc)). % to_list(Ral). to_list(L) -> tol(L,[]). from_list_loop([],Acc) -> Acc; from_list_loop([X|Xs],Acc) -> from_list_loop(Xs,cons(X,Acc)). % from_list(List). from_list(L) -> from_list_loop(lists:reverse(L),the_empty_ral). tree_iota(Da,1) -> {Da}; tree_iota(Da,Size) -> Half = Size div 2, {Da, tree_iota(Da+1,Half), tree_iota(Da+Half+1,Half)}. iota_loop([],Da) -> the_empty_ral; iota_loop([X|Xs],Da) -> [{X,tree_iota(Da,X)}|iota_loop(Xs,(Da+X))]. % iota(Int). iota(N) -> iota_loop(decompo(N),0). tree_map(F,{X}) -> {F(X)}; tree_map(F,{R,T1,T2}) -> {F(R), tree_map(F,T1), tree_map(F,T2)}. % map(Fun,Ral). map(F,the_empty_ral) -> the_empty_ral; map(F,[{Size,T1}|T2]) -> [{Size,tree_map(F,T1)}|map(F,T2)]. tree_iter(F,{X}) -> F(X); tree_iter(F,{X,T1,T2}) -> F(X), tree_iter(F,T1), tree_iter(F,T2). % iter(Fun,Ral). iter(F,the_empty_ral) -> the_empty_ral; iter(F,[{Size,T1}|T2]) -> tree_iter(F,T1), iter(F,T2). tree_for_all(F,{X}) -> F(X); tree_for_all(F,{X,T1,T2}) -> F(X) and tree_for_all(F,T1) and tree_for_all(F,T2). % for_all(Fun,Ral). for_all(F,the_empty_ral) -> true; for_all(F,[{Size,T1}|T2]) -> tree_for_all(F,T1) and for_all(F,T2). tree_exists(F,{X}) -> F(X); tree_exists(F,{X,T1,T2}) -> F(X) or tree_exists(F,T2) or tree_exists(F,T2). % exists(Fun,Ral). exists(F,the_empty_ral) -> false; exists(F,[{Size,T1}|T2]) -> tree_exists(F,T1) or exists(F,T2). tree_mem(X,{I}) when X =:= I -> true; tree_mem(X,{I}) -> false; tree_mem(X,{I,T1,T2}) when X =:= I -> true; tree_mem(X,{I,T1,T2}) -> tree_mem(X,T1) or tree_mem(X,T2). % mem(Item,Ral). mem(X,the_empty_ral) -> false; mem(X,[{Size,T1}|T2]) -> tree_mem(X,T1) or mem(X,T2). tree_split({{X,Y}}) -> {{X},{Y}}; tree_split({{X,Y},T1,T2}) -> {T11,T12} = tree_split(T1), {T21,T22} = tree_split(T2), {{X,T11,T21},{Y,T12,T22}}. % split(RalOfPairs). split(the_empty_ral) -> {the_empty_ral,the_empty_ral}; split([{Size,T1}|T2]) -> {T11,T12} = tree_split(T1), {T21,T22} = split(T2), {[{Size,T11}|T21],[{Size,T12}|T22]}. tree_combine({X1},{X2}) -> {{X1,X2}}; tree_combine({X1,T11,T21},{X2,T12,T22}) -> {{X1,X2}, tree_combine(T11,T12), tree_combine(T21,T22)}. % combine(Ral1,Ral2). combine(the_empty_ral,the_empty_ral) -> the_empty_ral; combine([{Size1,T11}|T21],[{Size2,T12}|T22]) when Size1 =:= Size2 -> [{Size1,tree_combine(T11,T12)}|combine(T21,T22)]. % end From daniel.neri@REDACTED Thu Oct 19 16:49:55 2000 From: daniel.neri@REDACTED (Daniel Neri) Date: 19 Oct 2000 16:49:55 +0200 Subject: job control with OTP R7B In-Reply-To: Ulf Wiger's message of "Thu, 19 Oct 2000 15:04:42 +0200 (MET DST)" References: Message-ID: Ulf Wiger writes: > I've noticed that if I hit Ctrl-Z (using tcsh) to suspend Erlang, > I'm having trouble getting OTP R7B back with 'fg'. FWIW, it works for me[TM], using any of bash, pdksh and even plain old csh (tested on NetBSD 1.4.2). Regards, --Daniel -- Daniel Neri mailto:dn@REDACTED Sigicom AB, Sweden http://www.sigicom.com From vladdu@REDACTED Thu Oct 19 16:49:40 2000 From: vladdu@REDACTED (Vlad Dumitrescu) Date: Thu, 19 Oct 2000 16:49:40 +0200 Subject: getting Erlang to run on an embedded powerpc 860 References: <14830.51782.915594.972691@corelatus.com> Message-ID: > Approach #1: Try to understand autoconf. Modify the configure > stuff so that it makes makefiles suitable for cross > compilation. After a frustrating day fighting with this > I decided to leave playing with autoconf for braver people. I never done it, but it should be possible to use autoconf and configure the build for cross-compilation.After all, it's a quite widely used practice! :) In any case, after my struggle with the NT build (which isn't over yet), I think the build process has a lot of room for improvement. Right now, there are still a lot of things that are hacked in. (that's not a criticism - I do that myself a lot!) regards, Vlad From etxuwig@REDACTED Thu Oct 19 16:50:09 2000 From: etxuwig@REDACTED (Ulf Wiger) Date: Thu, 19 Oct 2000 16:50:09 +0200 (MET DST) Subject: job control with OTP R7B In-Reply-To: Message-ID: I forgot to mention that I use tcsh on Solaris 7. /Uffe On 19 Oct 2000, Daniel Neri wrote: >Ulf Wiger writes: > >> I've noticed that if I hit Ctrl-Z (using tcsh) to suspend Erlang, >> I'm having trouble getting OTP R7B back with 'fg'. > >FWIW, it works for me[TM], using any of bash, pdksh and even plain old >csh (tested on NetBSD 1.4.2). > > >Regards, > --Daniel > > -- Ulf Wiger tfn: +46 8 719 81 95 Strategic Product & System Management mob: +46 70 519 81 95 Ericsson Telecom AB, Datacom Networks and IP Services Varuv?gen 9, ?lvsj?, S-126 25 Stockholm, Sweden From daniel.neri@REDACTED Thu Oct 19 17:17:15 2000 From: daniel.neri@REDACTED (Daniel Neri) Date: 19 Oct 2000 17:17:15 +0200 Subject: QNX port In-Reply-To: "Vlad Dumitrescu"'s message of "Sun, 1 Oct 2000 09:01:56 +0200" References: <200010010627.GAA19030@gecko.serc.rmit.edu.au> Message-ID: Hmm, I seem to have missed this message before... "Vlad Dumitrescu" writes: > I found the comments about porting Erlang 47.4.1, I'm guessing those were my comments... > and that no autoconf tweak was needed. Actually, autoconf tweaking *is* needed, but I took the easy way out -- I hacked the autoconf output instead. > However, I can't make it work... - configure cannot autodetect host > type. It's quite easy to add QNX autodetection by making some additions to the $ERL_TOP/erts/autoconf/config.{sub,guess} scripts. > - then make seems not being able to find the Makefiles in each > dir... This is a result of the first problem, since the configure script(s) put Makefiles in directories named after the host type. Best wishes, --Daniel -- Daniel Neri mailto:dn@REDACTED Sigicom AB, Sweden http://www.sigicom.com From mfs2@REDACTED Thu Oct 19 17:19:09 2000 From: mfs2@REDACTED (Mark Scandariato) Date: Thu, 19 Oct 2000 11:19:09 -0400 Subject: Porting Erlang Message-ID: <39EF10ED.A8F539A@lucent.com> Does anyone have any pointers on what it would take to port Erlang to VRTX? (http://www.mentor.com/embedded/vrtxos/index.html) The target system would be an MPC850 running VRTX and it would have only a boot flash - though a ram disk filesystem could be created from the flash image if needed. Mark Scandariato Lucent Technologies Raleigh, NC From Sean.Hinde@REDACTED Thu Oct 19 18:01:05 2000 From: Sean.Hinde@REDACTED (Sean Hinde) Date: Thu, 19 Oct 2000 17:01:05 +0100 Subject: QNX port Message-ID: <402DD461F109D411977E0008C791C312565231@imp02mbx.one2one.co.uk> > It's quite easy to add QNX autodetection by making some additions to > the $ERL_TOP/erts/autoconf/config.{sub,guess} scripts. The latest versions of config.{sub,guess} from freshmeat.net cover QNX. I found it should pretty much compile out of the box if you put these files in the right places in the build dirs. I have some more stuff to check out but with any luck should have it all running by the weekend. I'll post the changes I've made when I get there Sean NOTICE AND DISCLAIMER: This email (including attachments) is confidential. If you have received this email in error please notify the sender immediately and delete this email from your system without copying or disseminating it or placing any reliance upon its contents. We cannot accept liability for any breaches of confidence arising through use of email. Any opinions expressed in this email (including attachments) are those of the author and do not necessarily reflect our opinions. We will not accept responsibility for any commitments made by our employees outside the scope of our business. We do not warrant the accuracy or completeness of such information. From stimuli@REDACTED Fri Oct 20 02:49:34 2000 From: stimuli@REDACTED (Jeffrey Straszhiem) Date: Thu, 19 Oct 2000 20:49:34 -0400 Subject: Thoughts on laziness (long) In-Reply-To: <14830.63555.45918.561264@corelatus.com> References: <20001017194739.A1428@bzzt.shadow.net> <200010181111.NAA19866@trana.bluetail.com> <20001018211126.A1853@bzzt.shadow.net> <14830.63555.45918.561264@corelatus.com> Message-ID: <20001019204934.A1570@bzzt.shadow.net> On Thu, Oct 19, 2000 at 03:33:55PM +0200, matthias@REDACTED wrote: > Writing data structures in functional languages leads to > embarassment: almost no matter what you do, it's usually slower and > often also has worse big-O behaviour than the equivalent in a > language which allows destructive update. > One solution is to come up with elegant but complicated data > structures such as Okasaki's. Many of the more attractive ones > require lazy semantics. If you go down that road, you can brag on > comp.lang.functional that your functional language now also supports > monadical-recursive-slowdown-higher-order-transmogrification. You > also scare off many potential users. Well, I don't think the situation is quite as bad as that. > Another solution is to just provide hashtables a la ETS. Nowhere > near as elegant, but (a) it doesn't require turning the language's > semantics upside down and (b) it's reasonable to assume it's > faster*. Efficient dictionaries are a very important feature, and hash tables seem to be the only way to get them. And for that reason Erlang has made the right choice in throwing purity to the wind and providing ets tables and their like. And I agree that the quest for purity, in whole or part, must be for something more than bragging rights on c.l.f. I think what purity we provide must be there for some good reason, and the case seems to be the ease of understanding and maintaining functional code over imperative code. This argument is based on intangible factors, but in my experience it is very true. One thing your comparison did not point out is this data is only relevant in cases where you use the data in a single-threaded manner. By "single-threaded" here I don't mean to imply concurrency, but instead threads of data flow. Here is an example: This code uses a queue in a single-threaded manner: Queue1 = processQueue(Queue), Queue2 = processQueueAgain(Queue1), Queue2. This code uses the queue in a multi-threaded manner: Queue1 = processQueue(Queue), Queue2 = processQueueDifferently(Queue), {Queue1, Queue2}. With functional data structures, the second example does what you would expect. And, if neither function call has side effects, then the order that they're called doesn't matter either -- not that this is a very big deal, but when maintaining code it is nice not to have to worry about just where to place a computation to be between the correct side effects. This is easier with functional structures. You get none of the advantages with ets tables. So, except in cases where the performance cost is prohibitive, I would prefer to stick with functional data structures. Even if in you initial design you only use a structure in a single data flow thread, you may later decide to add, say, multi-level undo, or some other operation which is trivial to add to a functional structure. As with everything, there are tradeoffs. (I'm allowed one blatant truism per post :) Now back to the comments on laziness. In the single-threaded example above, the stdlib Erlang queues will do fine, giving good amortized behavior -- every so often they'll take a bit of time to reverse a list; the cost spreads out. However, as soon as you switch to the multi-threaded example, this fails, and you can lose the O(N) behavior, and in pathological cases end up with O(N^2) (this is from memory, Okasaki provides detailed analysis). In these cases laziness would prove useful. -- Jeffrey Straszheim | A sufficiently advanced -- Systems Engineer, Programmer | regular expression is -- http://www.shadow.net/~stimuli | indistinguishable from -- stimuli AT shadow DOT net | magic From etxuwig@REDACTED Fri Oct 20 09:40:24 2000 From: etxuwig@REDACTED (Ulf Wiger) Date: Fri, 20 Oct 2000 09:40:24 +0200 (MET DST) Subject: Thoughts on laziness (long) In-Reply-To: <20001019204934.A1570@bzzt.shadow.net> Message-ID: On Thu, 19 Oct 2000, Jeffrey Straszhiem wrote: >Efficient dictionaries are a very important feature, and hash tables >seem to be the only way to get them. And for that reason Erlang has >made the right choice in throwing purity to the wind and providing ets >tables and their like. Actually, you could view ets tables as an analogy to a server process holding the tables (or one process per table), updating a functional structure, and servicing insert/lookup requests via message passing. I wrote an Erlang-based ets implementation for the Erlang processor, and this is how it was done. My point is that you could well claim that the one impure feature of Erlang's is message passing. Ets tables bring nothing more to the table than what could be done using "classic" Erlang - it just does it faster. >One thing your comparison did not point out is this data is only >relevant in cases where you use the data in a single-threaded manner. >By "single-threaded" here I don't mean to imply concurrency, but >instead threads of data flow. Here is an example: Ehmm, I'm not too sure about this. If you have the case of a single process updating a private data structure, then you can do well with a functional data structure on the process's own heap, but as soon as you want to support concurrency, you need to place the data structure in a server process; then it's entirely up to that process to decide which granularity of concurrency you want. In order to support safe updates on ets tables, you'd funnel all updates through a server process, while atomic reads might go directly agains the ets table (for the functional structure, all operations must go through the same process). The performance comparison then stacks up the same way as for the single-threaded case, with an equivalent copying and scheduling overhead for both models; the one exception is that simple reads on an ets table can be optimized and go directly to the table, thus avoiding this overhead. As far as I can tell, this increases the advantage of ets tables over functional structures in the concurrent case. I'm not knocking Okasaki, but I don't think he factored in Erlang's concurrency model in his analysis. /Uffe -- Ulf Wiger tfn: +46 8 719 81 95 Strategic Product & System Management mob: +46 70 519 81 95 Ericsson Telecom AB, Datacom Networks and IP Services Varuv?gen 9, ?lvsj?, S-126 25 Stockholm, Sweden From arndt@REDACTED Fri Oct 20 09:59:03 2000 From: arndt@REDACTED (Arndt Jonasson) Date: 20 Oct 2000 07:59:03 GMT Subject: job control with OTP R7B References: , Message-ID: <8sou07$biv$1@news.du.uab.ericsson.se> In article , Daniel Neri wrote: >Ulf Wiger writes: > >> I've noticed that if I hit Ctrl-Z (using tcsh) to suspend Erlang, >> I'm having trouble getting OTP R7B back with 'fg'. > >FWIW, it works for me[TM], using any of bash, pdksh and even plain old >csh (tested on NetBSD 1.4.2). This is when it doesn't work: OTP R7, Solaris 2.6 or higher, and the emulator linked with -lpthread (which the commercial version is, but the open source version usually isn't). The shell is apparently not involved. We have a fix which seems to work. -- Arndt Jonasson Ericsson Utvecklings AB arndt@REDACTED ?T2/UAB/F/P +46 8 719 75 87 126 25 Stockholm From rv@REDACTED Fri Oct 20 10:24:54 2000 From: rv@REDACTED (Robert Virding) Date: Fri, 20 Oct 2000 10:24:54 +0200 Subject: Thoughts on laziness (long) In-Reply-To: Your message of "Fri, 20 Oct 2000 09:40:24 +0200." Message-ID: <200010200824.KAA27822@trana.bluetail.com> Ulf Wiger writes: >On Thu, 19 Oct 2000, Jeffrey Straszhiem wrote: > >>Efficient dictionaries are a very important feature, and hash tables >>seem to be the only way to get them. And for that reason Erlang has >>made the right choice in throwing purity to the wind and providing ets >>tables and their like. > >Actually, you could view ets tables as an analogy to a server process >holding the tables (or one process per table), updating a functional >structure, and servicing insert/lookup requests via message passing. >I wrote an Erlang-based ets implementation for the Erlang processor, >and this is how it was done. > >My point is that you could well claim that the one impure feature of >Erlang's is message passing. Ets tables bring nothing more to the >table than what could be done using "classic" Erlang - it just does it >faster. You took the words right out pf my mouth. This has been my point for quite a while that Erlang has really only one impure feature, and that is processes and message passing. Anything else can be viewed in terms of these. Like ETS tables. What I would have preferred with ETS tables is to have more explicit access to the primitive hash-table operations instead of bundling everything into one package. Robert -- Robert Virding Tel: +46 (0)8 545 55 017 Alteon Web Systems Email: rv@REDACTED S:t Eriksgatan 44 WWW: http://www.bluetail.com/~rv SE-112 34 Stockholm, SWEDEN "Folk s?ger att jag inte bryr mig om n?gonting, men det skiter jag i". From matthias@REDACTED Fri Oct 20 12:37:13 2000 From: matthias@REDACTED (matthias@REDACTED) Date: Fri, 20 Oct 2000 12:37:13 +0200 (CEST) Subject: Thoughts on laziness (long) In-Reply-To: References: <14830.63555.45918.561264@corelatus.com> Message-ID: <14832.8281.289164.409190@corelatus.com> Pierpaolo BERNARDI writes: > I too implemented ralists. If I remember correctly, my implementation > was about 5/6 times slower than built-in lists in a > cons/head/tail-based benchmark. Thanks for the code. Your implementation is cleaner and more complete than mine. Running it with the same test driver as I ran the other code, I get no significant difference in performance: N Sum Update Original ra-list ---------------------------------------- 100: 981 89 (962, 84) 1000: 9731 125 (9903, 127) 10000: 98k 183 (93k, 168) 100k: 1501k 248 (1357k, 188) Matthias From olivier@REDACTED Fri Oct 20 17:53:20 2000 From: olivier@REDACTED (Olivier Pomel) Date: Fri, 20 Oct 2000 11:53:20 -0400 Subject: What happened to Eddie ? Message-ID: <00d801c03aad$e7b66f20$26fea8c0@plato> What happened to Eddieware ? I can't reach the website anymore... Is the project still alive ? Olivier Pomel. From bjarne@REDACTED Fri Oct 20 17:59:39 2000 From: bjarne@REDACTED (Bjarne =?iso-8859-1?Q?D=E4cker?=) Date: Fri, 20 Oct 2000 17:59:39 +0200 Subject: What happened to Eddie ? References: <00d801c03aad$e7b66f20$26fea8c0@plato> Message-ID: <39F06BEB.C6CD0101@erix.ericsson.se> Olivier Pomel wrote: > > What happened to Eddieware ? I can't reach the website anymore... > Is the project still alive ? Did you try http://www.eddieware.org/ They also have a commercial branch http://www.lodbroker.com/ Good luck Bjarne From stimuli@REDACTED Sat Oct 21 03:44:41 2000 From: stimuli@REDACTED (Jeffrey Straszhiem) Date: Fri, 20 Oct 2000 21:44:41 -0400 Subject: Thoughts on laziness (long) In-Reply-To: References: <20001019204934.A1570@bzzt.shadow.net> Message-ID: <20001020214441.A1373@bzzt.shadow.net> On Fri, Oct 20, 2000 at 09:40:24AM +0200, Ulf Wiger wrote: > My point is that you could well claim that the one impure feature of > Erlang's is message passing. Ets tables bring nothing more to the > table than what could be done using "classic" Erlang - it just does > it faster. True. Actually I had assumed, without looking at the implementation, that ets tables were indeed processes as you describe. I guess I learn something new everyday. :) >> One thing your comparison did not point out is this data is only >> relevant in cases where you use the data in a single-threaded >> manner. By "single-threaded" here I don't mean to imply >> concurrency, but instead threads of data flow. Here is an example: > Ehmm, I'm not too sure about this. If you have the case of a single > process updating a private data structure, then you can do well with > a functional data structure on the process's own heap, but as soon > as you want to support concurrency, you need to place the data > structure in a server process; then it's entirely up to that process > to decide which granularity of concurrency you want. In order to > support safe updates on ets tables, you'd funnel all updates through > a server process, while atomic reads might go directly agains the > ets table (for the functional structure, all operations must go > through the same process). Again true. In many ways Erlang server processes can end up looking alot like objects with state, and not so much like "processes" at all. I think your ets as a server example proves this point. However, within an implementation of a server process there may arise a case where a complex computation might need to be performed. If this sort of computation is the kind that would benefit from laziness then it would pay off. That being said, I'm not sure how common the payoff would be. It is certainly possible that laziness in Erlang would not be worth its cost. On the other hand, simply providing 'suspend' and 'force' operators to the language shouldn't be too difficult. Newcomers to the language could produce plenty of code without them -- I really imagine them used by experts to build data structure modules -- so they need not impede the simplicity of learning. Anyhow, surely everyone will agree that it is worth more thought. -- Jeffrey Straszheim | A sufficiently advanced -- Systems Engineer, Programmer | regular expression is -- http://www.shadow.net/~stimuli | indistinguishable from -- stimuli AT shadow DOT net | magic From vladdu@REDACTED Mon Oct 23 10:39:42 2000 From: vladdu@REDACTED (Vlad Dumitrescu) Date: Mon, 23 Oct 2000 10:39:42 +0200 Subject: supervising non-Erlang apps Message-ID: Hi all! Is it possible (out-of-the-box or with some effort) to use an Erlang supervising app to handle not only Erlang nodes and processes, but also legacy applications? (cannot recompile them in any way) thanks in advance Vlad -------------- next part -------------- An HTML attachment was scrubbed... URL: From etxuwig@REDACTED Mon Oct 23 10:51:12 2000 From: etxuwig@REDACTED (Ulf Wiger) Date: Mon, 23 Oct 2000 10:51:12 +0200 (MET DST) Subject: supervising non-Erlang apps In-Reply-To: Message-ID: On Mon, 23 Oct 2000, Vlad Dumitrescu wrote: >Hi all! > >Is it possible (out-of-the-box or with some effort) to use an Erlang >supervising app to handle not only Erlang nodes and processes, but >also legacy applications? (cannot recompile them in any way) > >thanks in advance >Vlad Yes, it's usually possible. If the legacy app uses stdio, you can start it using open_port, and then let an Erlang process monitor the port. This way, you will have basic supervision of the application, and can restart it if it crashes. Beyond that, it's very much a question of how you can interface with the application for configuration and control. /Uffe -- Ulf Wiger tfn: +46 8 719 81 95 Strategic Product & System Management mob: +46 70 519 81 95 Ericsson Telecom AB, Datacom Networks and IP Services Varuv?gen 9, ?lvsj?, S-126 25 Stockholm, Sweden From Sean.Hinde@REDACTED Mon Oct 23 10:51:44 2000 From: Sean.Hinde@REDACTED (Sean Hinde) Date: Mon, 23 Oct 2000 09:51:44 +0100 Subject: Binary guards problem Message-ID: <402DD461F109D411977E0008C791C31256523B@imp02mbx.one2one.co.uk> All, There seems to be a small problem with binaries in R7B: test(<>) when size(B) < 4 -> small; test(<<1, B/binary>>) -> large. calling test(<<1, 2>>) returns large calling test(<<2, 2>>) returns small It works fine in the debugger.. Sean NOTICE AND DISCLAIMER: This email (including attachments) is confidential. If you have received this email in error please notify the sender immediately and delete this email from your system without copying or disseminating it or placing any reliance upon its contents. We cannot accept liability for any breaches of confidence arising through use of email. Any opinions expressed in this email (including attachments) are those of the author and do not necessarily reflect our opinions. We will not accept responsibility for any commitments made by our employees outside the scope of our business. We do not warrant the accuracy or completeness of such information. From Chandrashekhar.Mullaparthi@REDACTED Mon Oct 23 11:06:39 2000 From: Chandrashekhar.Mullaparthi@REDACTED (Chandrashekhar Mullaparthi) Date: Mon, 23 Oct 2000 10:06:39 +0100 Subject: Binary guards problem Message-ID: <402DD461F109D411977E0008C791C31202A796E1@imp02mbx.one2one.co.uk> It works fine when defined as a fun in the erlang shell. But when compiled in a module it's the same behavoiur as Sean has pointed out. -----Original Message----- From: Sean Hinde [mailto:Sean.Hinde@REDACTED] Sent: 23 October 2000 09:52 To: 'Erlang Questions' Subject: Binary guards problem All, There seems to be a small problem with binaries in R7B: test(<>) when size(B) < 4 -> small; test(<<1, B/binary>>) -> large. calling test(<<1, 2>>) returns large calling test(<<2, 2>>) returns small It works fine in the debugger.. Sean NOTICE AND DISCLAIMER: This email (including attachments) is confidential. If you have received this email in error please notify the sender immediately and delete this email from your system without copying or disseminating it or placing any reliance upon its contents. We cannot accept liability for any breaches of confidence arising through use of email. Any opinions expressed in this email (including attachments) are those of the author and do not necessarily reflect our opinions. We will not accept responsibility for any commitments made by our employees outside the scope of our business. We do not warrant the accuracy or completeness of such information. NOTICE AND DISCLAIMER: This email (including attachments) is confidential. If you have received this email in error please notify the sender immediately and delete this email from your system without copying or disseminating it or placing any reliance upon its contents. We cannot accept liability for any breaches of confidence arising through use of email. Any opinions expressed in this email (including attachments) are those of the author and do not necessarily reflect our opinions. We will not accept responsibility for any commitments made by our employees outside the scope of our business. We do not warrant the accuracy or completeness of such information. From matthias@REDACTED Mon Oct 23 11:35:40 2000 From: matthias@REDACTED (matthias@REDACTED) Date: Mon, 23 Oct 2000 11:35:40 +0200 (CEST) Subject: Thoughts on laziness (long) In-Reply-To: References: <14830.63555.45918.561264@corelatus.com> Message-ID: <14836.1644.552248.429240@corelatus.com> > Advantages of gb-trees are that they are much more lightweight (an empty > tree is a tuple {0, nil}, so you can create and throw away as many as you > like on the fly, and that they do no copying, since they reside on the > local heap - you could try the same test inserting/updating/fetching some > bigger things than integers and see the difference. Yes. If I use larger erlang structures, e.g. large tuples, large lists, there's a marked slowdown in ETS. (If I avoid copying by using binaries, there's no slowdown). > They should perform better in native code compared to ETS than under the > BEAM emulator, but in general the complexity of the algorithms would > remain the same. I remember reading that the 'Mercury' people hoped to be able to perform complexity reducing source->source transforms. Does HIPE do that, or does the potential (I read "in general" to mean "there are some cases where we can do better". That may have been reading too much into what you wrote.) come from 'striking it lucky' when doing (local) optimisations on object code? Matthias From bjarne@REDACTED Mon Oct 23 11:36:31 2000 From: bjarne@REDACTED (Bjarne =?iso-8859-1?Q?D=E4cker?=) Date: Mon, 23 Oct 2000 11:36:31 +0200 Subject: EUC photographs Message-ID: <39F4069F.4E991922@erix.ericsson.se> Hello Photographs from the Erlang/OTP user conference http://www.erlang.se/euc/00/pics.html are now available together with the proceedings http://www.erlang.se/euc/00/ Best regards Bjarne From bjorn@REDACTED Mon Oct 23 11:37:19 2000 From: bjorn@REDACTED (Bjorn Gustavsson) Date: 23 Oct 2000 11:37:19 +0200 Subject: Binary guards problem In-Reply-To: Sean Hinde's message of "Mon, 23 Oct 2000 09:51:44 +0100" References: <402DD461F109D411977E0008C791C31256523B@imp02mbx.one2one.co.uk> Message-ID: It is a compiler problem. I'm working on a correction. Thanks for pointing out the error. /Bj?rn Sean Hinde writes: > All, > > There seems to be a small problem with binaries in R7B: > > test(<>) when size(B) < 4 -> > small; > test(<<1, B/binary>>) -> > large. > > calling test(<<1, 2>>) returns large > calling test(<<2, 2>>) returns small > > It works fine in the debugger.. > > Sean > > > > NOTICE AND DISCLAIMER: > This email (including attachments) is confidential. If you have received > this email in error please notify the sender immediately and delete this > email from your system without copying or disseminating it or placing any > reliance upon its contents. We cannot accept liability for any breaches of > confidence arising through use of email. Any opinions expressed in this > email (including attachments) are those of the author and do not necessarily > reflect our opinions. We will not accept responsibility for any commitments > made by our employees outside the scope of our business. We do not warrant > the accuracy or completeness of such information. > > -- Bj?rn Gustavsson Ericsson Utvecklings AB bjorn@REDACTED ?T2/UAB/F/P BOX 1505 +46 8 727 56 87 125 25 ?lvsj? From pparamas@REDACTED Mon Oct 23 11:55:47 2000 From: pparamas@REDACTED (Priya Paramasevam) Date: Mon, 23 Oct 2000 20:55:47 +1100 (EST) Subject: GS application Message-ID: Hi, I am using Erlang to develop a simulation. I need to use GS for this. I have installed Erlang and GS on my computer. Unfortunately, I am not able to view the GS window. Kind Regards, Priya Paramasevam Master of Applied Science (IT) RMIT University, Melbourne, Australia. ========================================================================== A good sense of humour, a big dose of patience, and a dash of humility, you will be rewarded many-fold! ========================================================================== From richardc@REDACTED Mon Oct 23 12:00:35 2000 From: richardc@REDACTED (Richard Carlsson) Date: Mon, 23 Oct 2000 12:00:35 +0200 (MET DST) Subject: Thoughts on laziness (long) In-Reply-To: <14836.1644.552248.429240@corelatus.com> Message-ID: On Mon, 23 Oct 2000 matthias@REDACTED wrote: > > They should perform better in native code compared to ETS than > > under the BEAM emulator, but in general the complexity of the > > algorithms would remain the same. > > I remember reading that the 'Mercury' people hoped to be able to > perform complexity reducing source->source transforms. Does HIPE do > that, or does the potential (I read "in general" to mean "there are > some cases where we can do better". That may have been reading too > much into what you wrote.) come from 'striking it lucky' when doing > (local) optimisations on object code? What I meant by that slightly opaque remark was that there are a number of things we want to do in HiPE that are not being done today, such as type analysis (in order to remove some runtime type tests), function inlining and other optimizations on high-level intermediate code (Core Erlang), using multiple return values instead of building intermediate heap objects ("{X, Y, Z} = foo(...), bar(X, Y, Z)"), and even partial evaluation. However, these things all give linear speedup (no matter how lucky you get). There *exist* some transformations which can give superlinear speedup (i.e., changing the complexity), but typically, these are difficult to control (you don't want your compiler to give unexpected code size explosions) and are not necessarily useful for typical programs. They are interesting, but are not a priority for us right now. /Richard Carlsson Richard Carlsson (richardc@REDACTED) (This space intentionally left blank.) E-mail: Richard.Carlsson@REDACTED WWW: http://www.csd.uu.se/~richardc/ From thomasl@REDACTED Mon Oct 23 11:53:23 2000 From: thomasl@REDACTED (Thomas Lindgren) Date: Mon, 23 Oct 2000 11:53:23 +0200 Subject: Thoughts on laziness (long) In-Reply-To: <14836.1644.552248.429240@corelatus.com> (matthias@corelatus.com) References: <14830.63555.45918.561264@corelatus.com> <14836.1644.552248.429240@corelatus.com> Message-ID: <200010230953.LAA01577@lammgam.bluetail.com> > I remember reading that the 'Mercury' people hoped to be able to > perform complexity reducing source->source transforms. Does HIPE do > that, or does the potential (I read "in general" to mean "there are > some cases where we can do better". That may have been reading too > much into what you wrote.) come from 'striking it lucky' when doing > (local) optimisations on object code? As far as I am aware, the HIPE compiler does not reduce complexity apart from removing simple dead computations. For example, it can't remove a loop (exception-free, etc) producing an unused value. Or at least it couldn't when I left Uppsala. Maybe there are some new tricks I'm not aware of. The Mercury compiler (among others) is capable of one form of optimization that should be possible at nearly-source level: reordering of conses (and other term constructions). This reduces space complexity, e.g., from linear into running at constant stack space. (Prolog implementations tend to use this by default, btw.) Here is an illustration of the basic technique. Take simple append: append([],Ys) -> Ys; append([X|Xs],Ys) -> Zs = append(Xs,Ys), [X|Zs]. The code above pushes X on the stack in each append iteration, then constructs the list one step at a time as the stack pops, when append/2 returns. You can say this constructs the list back-to-front: the last cons of the (new) list is the first to be constructed This code can basically be rewritten into the more efficient: append([],Ys) -> Ys; append([X|Xs],Ys) -> Res = [X|empty], append_loop(Xs,Ys,Res). append_loop([],Ys,Tail) -> setcdr(Tail,Ys); %% setcdr not really in erlang append_loop([X|Xs],Ys,Tail) -> Res = [X|empty], setcdr(Tail,Res), append_loop(Xs,Ys,Res). Here, we construct the list 'front-to-back' instead. We keep a pointer to the last cons of the list (which is incomplete, as indicated by 'empty') and zap it with the next cons. What is the gain? append_loop/3 is tail recursive. At each iteration, append/2 saves (a) a return address and (b) X. Thus, we save 2 stores + 2 loads per list element, which isn't bad for append. Thomas -- Thomas Lindgren thomasl+junk@REDACTED Alteon Websystems Sweden http://www.bluetail.com From luke@REDACTED Mon Oct 23 12:07:24 2000 From: luke@REDACTED (Luke Gorrie) Date: 23 Oct 2000 12:07:24 +0200 Subject: Binary guards problem In-Reply-To: Sean Hinde's message of "Mon, 23 Oct 2000 09:51:44 +0100" References: <402DD461F109D411977E0008C791C31256523B@imp02mbx.one2one.co.uk> Message-ID: Sean Hinde writes: > All, > > There seems to be a small problem with binaries in R7B: > > test(<>) when size(B) < 4 -> > small; > test(<<1, B/binary>>) -> > large. > > calling test(<<1, 2>>) returns large > calling test(<<2, 2>>) returns small > > It works fine in the debugger.. I've noticed another quirk, where for some cases "Bin1 = Bin2" fails with a badmatch, but "true = Bin1 == Bin2" succeeds. Is this a bug? If so I can send some code to reproduce it. -Luke From bjorn@REDACTED Mon Oct 23 12:20:24 2000 From: bjorn@REDACTED (Bjorn Gustavsson) Date: 23 Oct 2000 12:20:24 +0200 Subject: Binary guards problem In-Reply-To: Luke Gorrie's message of "23 Oct 2000 12:07:24 +0200" References: <402DD461F109D411977E0008C791C31256523B@imp02mbx.one2one.co.uk> Message-ID: Luke Gorrie writes: > Sean Hinde writes: > > > All, > > > > There seems to be a small problem with binaries in R7B: > > > > test(<>) when size(B) < 4 -> > > small; > > test(<<1, B/binary>>) -> > > large. > > > > calling test(<<1, 2>>) returns large > > calling test(<<2, 2>>) returns small > > > > It works fine in the debugger.. > > I've noticed another quirk, where for some cases "Bin1 = Bin2" fails > with a badmatch, but "true = Bin1 == Bin2" succeeds. Is this a bug? If > so I can send some code to reproduce it. Yes, I would like to see the code that reproduces the error. /Bj?rn -- Bj?rn Gustavsson Ericsson Utvecklings AB bjorn@REDACTED ?T2/UAB/F/P BOX 1505 +46 8 727 56 87 125 25 ?lvsj? From etxuwig@REDACTED Mon Oct 23 12:41:15 2000 From: etxuwig@REDACTED (Ulf Wiger) Date: Mon, 23 Oct 2000 12:41:15 +0200 (MET DST) Subject: non-Erlang Erlang grammars Message-ID: I've spent some time pondering the match specifications for trace and (not yet released) ets. While the functionality is powerful and much wanted, I think it's really too bad that one can't specify the filters using Erlang pattern matching syntax, which is already so familiar. Also, when speaking of foldl-type operations on ets tables, it would be really nice if one could write a fun in Erlang which i guaranteed to be O(1), and thus could be executed efficiently by the VM in an ets:foldl(). One advantage of the ugly (sorry) match specs is that they can be generated quite easily from a program I'm working on such a program right now: match_conditions([{V1,V2}|Vars], [D|Dims], KeyComps, Pat) -> case element(D, Pat) of range -> K = {{element(D, KeyComps)}}, [{'=<', V1, K}, {'>', V2, K} | match_conditions(Vars, Dims, KeyComps, Pat)]; {match, upper, X} -> [{'==', V2, X} | match_conditions(Vars, Dims, KeyComps, Pat)]; {match, lower, X} -> [{'==', V1, X} | match_conditions(Vars, Dims, KeyComps, Pat)]; {match, both_equal, Pair} -> [{'==', {{V1,V2}}, {Pair}} | match_conditions(Vars, Dims, KeyComps, Pat)] end; match_conditions([], [], _, _) -> []. but I still think there should be a way to achieve this without departing too much from the standard Erlang grammar. Lastly: If we are supposed to write functions to this effect, it would be nice to have a library function, or BIF, to solve the following: var( 1) -> '$1'; var( 2) -> '$2'; var( 3) -> '$3'; var( 4) -> '$4'; var( 5) -> '$5'; var( 6) -> '$6'; var( 7) -> '$7'; var( 8) -> '$8'; var( 9) -> '$9'; var(10) -> '$10'; var(11) -> '$11'; var(12) -> '$12'; var(13) -> '$13'; var(14) -> '$14'; var(15) -> '$15'; var(16) -> '$16'; var(17) -> '$17'; var(18) -> '$18'; var(19) -> '$19'; var( N) -> list_to_atom([$$|integer_to_list(N)]). Obviously, I'd rather see that the need for such "hash variables" went away completely. /Uffe -- Ulf Wiger tfn: +46 8 719 81 95 Strategic Product & System Management mob: +46 70 519 81 95 Ericsson Telecom AB, Datacom Networks and IP Services Varuv?gen 9, ?lvsj?, S-126 25 Stockholm, Sweden From etxuwig@REDACTED Mon Oct 23 12:56:25 2000 From: etxuwig@REDACTED (Ulf Wiger) Date: Mon, 23 Oct 2000 12:56:25 +0200 (MET DST) Subject: fun complexity analysis (Re: non-Erlang Erlang grammars) In-Reply-To: Message-ID: On Mon, 23 Oct 2000, Ulf Wiger wrote: >Also, when speaking of foldl-type operations on ets tables, it would >be really nice if one could write a fun in Erlang which i guaranteed >to be O(1), and thus could be executed efficiently by the VM in an >ets:foldl(). Not being a compiler writer, perhaps I don't realise the problems associated with this, but shouldn't it be rather straightforward to verify that a fun has the following properties: - no message passing - no external function calls - no loops - no operations that are not known to be O(1) These functions could then be used in match filters (perhaps even guards!) and action specifications in, e.g., a trace filter specification. (Lifting the message passing restriction, it would also be nice to be able to require that event handlers have these characteristics - and subsequently, I would be able to call a fun in a way that I get an exception if the fun is not _known_ to be an O(1) fun. This could perhaps be done using the "annotation" syntax in Core Erlang?) /Uffe -- Ulf Wiger tfn: +46 8 719 81 95 Strategic Product & System Management mob: +46 70 519 81 95 Ericsson Telecom AB, Datacom Networks and IP Services Varuv?gen 9, ?lvsj?, S-126 25 Stockholm, Sweden From thomasl@REDACTED Mon Oct 23 13:07:29 2000 From: thomasl@REDACTED (Thomas Lindgren) Date: Mon, 23 Oct 2000 13:07:29 +0200 Subject: fun complexity analysis (Re: non-Erlang Erlang grammars) In-Reply-To: (message from Ulf Wiger on Mon, 23 Oct 2000 12:56:25 +0200 (MET DST)) References: Message-ID: <200010231107.NAA01904@lammgam.bluetail.com> > - no message passing > - no external function calls > - no loops > - no operations that are not known to be O(1) > > These functions could then be used in match filters (perhaps even > guards!) and action specifications in, e.g., a trace filter > specification. Note that there is at least one guard that is not O(1), length/1. Also nodes/1, I guess. All of the above properties are straightforward to verify (you need a table of O()-properties for BIFs). One shouldn't permit use of apply/spawn/spawn_link/F(X) in all their varieties either. Put (and get?) are out as well. In short, all side-effect operations. What about exceptions? For user-defined guards, one could wrap the operation in an "exception => fail" construct as in Core Erlang. For other operations (such as ets:foldl()), it might not be a problem. Thomas -- Thomas Lindgren thomasl+junk@REDACTED Alteon Websystems Sweden http://www.bluetail.com From rv@REDACTED Mon Oct 23 15:12:19 2000 From: rv@REDACTED (Robert Virding) Date: Mon, 23 Oct 2000 15:12:19 +0200 Subject: non-Erlang Erlang grammars In-Reply-To: Your message of "Mon, 23 Oct 2000 12:41:15 +0200." Message-ID: <200010231312.PAA31433@trana.bluetail.com> Ulf Wiger writes: > >I've spent some time pondering the match specifications for trace >and (not yet released) ets. While the functionality is powerful >and much wanted, I think it's really too bad that one can't specify >the filters using Erlang pattern matching syntax, which is already so >familiar. > One thing I thought might be a good way to solve this this problem would be to use funs, BUT be extremely restrictive in what is allowed within the fun, basically matching and simple BIFs. No function calls at all or data creation either. This checking could be done at run time so no extra langauge constructs or definitions are necessary. What it can't do is allow you to construct match specifications on the fly. The problem with all this is of course that as an ets table doesn't exist within any process there is no context in which to evaluate the fun. Therefore you have to be extremely restrictive. With a bit of trickery you could probably allow calls to local functions as long as they obey the ame restricitive rules. To handle recursion just allow at most one time slice per check, anything longer results in a fail, or error. While this operationally might not give you more than match specs it at least uses existing features in a relatively clean way. And I agree, match specs are ugly. Robert -- Robert Virding Tel: +46 (0)8 545 55 017 Alteon Web Systems Email: rv@REDACTED S:t Eriksgatan 44 WWW: http://www.bluetail.com/~rv SE-112 34 Stockholm, SWEDEN "Folk s?ger att jag inte bryr mig om n?gonting, men det skiter jag i". From bjorn@REDACTED Mon Oct 23 15:15:38 2000 From: bjorn@REDACTED (Bjorn Gustavsson) Date: 23 Oct 2000 15:15:38 +0200 Subject: Binary guards problem In-Reply-To: Luke Gorrie's message of "23 Oct 2000 14:04:09 +0200" References: <402DD461F109D411977E0008C791C31256523B@imp02mbx.one2one.co.uk> Message-ID: Luke Gorrie writes: > Bjorn Gustavsson writes: > > > Yes, I would like to see the code that reproduces the error. > > Here is a better example: > > Eshell V5.0.1 (abort with ^G) > 1> A = <<1, 2, 3>>. > <<1,2,3>> > 2> B = <<4, 5, 6>>. > <<4,5,6>> > 3> <> = <<1, 2, 3, 4, 5, 6>>. > ** exited: {{badmatch,<<1,2,3,4,5,6>>},[{erl_eval,expr,3}]} ** Equvivalent to <> = <<1, 2, 3, 4, 5, 6>> > 4> <> = <<1, 2, 3, 4, 5, 6>>. > ** exited: {{badmatch,<<1,2,3,4,5,6>>},[{erl_eval,expr,3}]} ** If you would have used unbound variables, you would have got: A = <<1, 2, 3, 4, 5, 6>> B = <<>> Since A and B were bound, what will be matched is <<1, 2, 3>> = <<1, 2, 3, 4, 5, 6>> You must write Sz = size(A). <> = <<1,2,3,4,5,6>>. to get the result you want. > 5> <> == <<1, 2, 3, 4, 5, 6>>. > true > /Bj?rn -- Bj?rn Gustavsson Ericsson Utvecklings AB bjorn@REDACTED ?T2/UAB/F/P BOX 1505 +46 8 727 56 87 125 25 ?lvsj? From etxuwig@REDACTED Mon Oct 23 15:22:54 2000 From: etxuwig@REDACTED (Ulf Wiger) Date: Mon, 23 Oct 2000 15:22:54 +0200 (MET DST) Subject: non-Erlang Erlang grammars In-Reply-To: <200010231312.PAA31433@trana.bluetail.com> Message-ID: On Mon, 23 Oct 2000, Robert Virding wrote: >While this operationally might not give you more than match specs it >at least uses existing features in a relatively clean way. And I >agree, match specs are ugly. This is a very important aspect, though. Preserving the elegance of the language is worth a lot. It's really a shame if we end up where the only time you ever get to write beautiful Erlang code is in the introductory course. /Uffe -- Ulf Wiger tfn: +46 8 719 81 95 Strategic Product & System Management mob: +46 70 519 81 95 Ericsson Telecom AB, Datacom Networks and IP Services Varuv?gen 9, ?lvsj?, S-126 25 Stockholm, Sweden From kent@REDACTED Mon Oct 23 17:39:36 2000 From: kent@REDACTED (Kent Boortz) Date: 23 Oct 2000 17:39:36 +0200 Subject: Porting Erlang In-Reply-To: Mark Scandariato's message of "Thu, 19 Oct 2000 11:19:09 -0400" References: <39EF10ED.A8F539A@lucent.com> Message-ID: > Does anyone have any pointers on what it would take to port Erlang > to VRTX? (http://www.mentor.com/embedded/vrtxos/index.html) > > The target system would be an MPC850 running VRTX and it would have > only a boot flash - though a ram disk filesystem could be created > from the flash image if needed. I don't think there is anything written about porting Erlang to a new embedded target. There have been prototype ports to some embedded operating systems. The only supported embedded port is VxWorks. Erlang/OTP is a bit POSIX/Unix oriented but it is possible to port it to a non POSIX OS as well, only a bit harder. The Window port shows it can be done. Some comments: - VRTX seem to have a POSIX library and a complete TCP/IP stack so the libraries needed are probably there. Erlang put some high demands on the socket library so if there are some strange bugs in VRTX TCP/IP stack, especially with non blocking I/O, then you will probably run into it. - All C files, except "beam_emu.c", should compile with any ANSI C compiler. We compile "beam_emu.c" with GCC to use a special GCC feature that makes the decoding of the Erlang virtual instructions twice as fast. But you should be able to compile without GCC if you define the preprocessor symbol NO_JUMP_TABLE. You define it by running make like % gmake TYPE_FLAGS="-g -O2 -DNO_JUMP_TABLE" Also note that "beam_emu.c" contains a huge switch statement, your C compiler or your machine setup may not be able to handle it. If not, try disabling the optimizations first, "-O1" or even "-O0" but this will of cause reduce the speed even more. I think it is possible to use GCC for compiling to a VRTX target but haven't found any real prof of this. - I assume you have to cross compile. The usual configure script does not handle this. Look at the VxWork specific files and do something similar. - The Erlang distribution, i.e. how Erlang nodes communicate, normally use TCP/IP. But you can write your own driver and enable Erlang nodes to use the native message passing in VRTX. I don't know how much work this is but the process is documented. kent From luke@REDACTED Mon Oct 23 18:34:12 2000 From: luke@REDACTED (Luke Gorrie) Date: 23 Oct 2000 18:34:12 +0200 Subject: Binary guards problem In-Reply-To: Bjorn Gustavsson's message of "23 Oct 2000 15:15:38 +0200" References: <402DD461F109D411977E0008C791C31256523B@imp02mbx.one2one.co.uk> Message-ID: Bjorn Gustavsson writes: > You must write > > Sz = size(A). > > <> = <<1,2,3,4,5,6>>. > > to get the result you want. Might it be more intuitive if the :Sz/binary bit was implicit if the variable is bound to a binary? Maybe it's not important, and I haven't looked at the code so perhaps I know not what I ask. :-) Cheers, Luke From mfs2@REDACTED Mon Oct 23 20:39:23 2000 From: mfs2@REDACTED (Mark Scandariato) Date: Mon, 23 Oct 2000 14:39:23 -0400 Subject: Porting Erlang References: <39EF10ED.A8F539A@lucent.com> Message-ID: <39F485DB.EDDF7942@lucent.com> Kent Boortz wrote: > > > Does anyone have any pointers on what it would take to port Erlang > > to VRTX? (http://www.mentor.com/embedded/vrtxos/index.html) > > > > The target system would be an MPC850 running VRTX and it would have > > only a boot flash - though a ram disk filesystem could be created > > from the flash image if needed. > > I don't think there is anything written about porting Erlang to a new > embedded target. There have been prototype ports to some embedded > operating systems. The only supported embedded port is VxWorks. > > Erlang/OTP is a bit POSIX/Unix oriented but it is possible to port it > to a non POSIX OS as well, only a bit harder. The Window port shows it > can be done. > > Some comments: > > - VRTX seem to have a POSIX library and a complete TCP/IP stack so > the libraries needed are probably there. Erlang put some high > demands on the socket library so if there are some strange bugs in > VRTX TCP/IP stack, especially with non blocking I/O, then you will > probably run into it. > > - All C files, except "beam_emu.c", should compile with any ANSI C > compiler. We compile "beam_emu.c" with GCC to use a special GCC > feature that makes the decoding of the Erlang virtual instructions > twice as fast. But you should be able to compile without GCC if > you define the preprocessor symbol NO_JUMP_TABLE. You define it by > running make like > > % gmake TYPE_FLAGS="-g -O2 -DNO_JUMP_TABLE" > > Also note that "beam_emu.c" contains a huge switch statement, > your C compiler or your machine setup may not be able to handle > it. If not, try disabling the optimizations first, "-O1" or > even "-O0" but this will of cause reduce the speed even more. > I think it is possible to use GCC for compiling to a VRTX target > but haven't found any real prof of this. > > - I assume you have to cross compile. The usual configure script > does not handle this. Look at the VxWork specific files and > do something similar. > > - The Erlang distribution, i.e. how Erlang nodes communicate, > normally use TCP/IP. But you can write your own driver and enable > Erlang nodes to use the native message passing in VRTX. I don't > know how much work this is but the process is documented. > > kent Thanks for the pointers. I'm also evaluating LynxOS (http://www.lynuxworks.com/products/whatislos.html) which appears to be much more POSIX/Unix flavored than VRTX and VxWorks, and fully supports the GNU toolchain. VRTX & LynxOS have very similar features - the biggest of which are memory protection, deterministic behavior, and interruptable system calls - that VxWorks doesn't. Mark. From alexis@REDACTED Tue Oct 24 02:19:02 2000 From: alexis@REDACTED (=?iso-8859-1?Q?Alexis_L=EA-Qu=F4c?=) Date: Mon, 23 Oct 2000 20:19:02 -0400 Subject: Erlang Consulting/support/training in the US? Message-ID: <001a01c03d50$036320c0$8cfea8c0@neomeo.com> Greetings, I'm looking for people/companies providing consulting and training in the US. How do I find if there are any? -- Alexis L?-Qu?c alexis@REDACTED http://www.neomeo.com From Gerald.Biederbeck@REDACTED Tue Oct 24 08:03:41 2000 From: Gerald.Biederbeck@REDACTED (Gerald Biederbeck) Date: Tue, 24 Oct 2000 08:03:41 +0200 Subject: GS application References: Message-ID: <39F5263D.F0C3F504@eede.ericsson.se> Hi, I installed GS and Erlang too. It works fine. ;-) BR /Gerry Priya Paramasevam wrote: > > Hi, > > I am using Erlang to develop a simulation. I need to use GS for this. I > have installed Erlang and GS on my computer. Unfortunately, I am not able > to view the GS window. > > Kind Regards, > Priya Paramasevam > Master of Applied Science (IT) > RMIT University, > Melbourne, Australia. > > > ========================================================================== > A good sense of humour, a big dose of patience, and a dash of humility, > you will be rewarded many-fold! > ========================================================================== From vladdu@REDACTED Tue Oct 24 08:59:02 2000 From: vladdu@REDACTED (Vlad Dumitrescu) Date: Tue, 24 Oct 2000 08:59:02 +0200 Subject: non-Erlang Erlang grammars References: <200010231312.PAA31433@trana.bluetail.com> Message-ID: ----- Original Message ----- From: "Robert Virding" Ulf Wiger writes: >I've spent some time pondering the match specifications for trace >and (not yet released) ets. While the functionality is powerful >and much wanted, I think it's really too bad that one can't specify >the filters using Erlang pattern matching syntax, which is already so >familiar. One thing I thought might be a good way to solve this this problem would be to use funs, BUT be extremely restrictive in what is allowed within the fun, basically matching and simple BIFs. No function calls at all or data creation either. --------- I was thinking about this problem, and I suppose the solution using funs would be to provide an ets:lookup/3, where the third argument is a fun that filters the results of ets:lookup/2 and also filters the elements that will be outputted to the result. Also, list comprehensions that accept an ets generator would also do fine, even more elegantly than simple funs. As I understand it, the big problem is how to make the funs evaluate in ets space, not local space. But otherwise I can't understand why these funs should abide by the following rules >- no message passing >- no external function calls >- no loops >- no operations that are not known to be O(1) Is it an efficiency aspect? Or an implementation issue? Or a more deep-going architecture one? Is it because the funs woill be evaluated in a different space? Could someone please explain this? Thanks. Vlad From simonb@REDACTED Tue Oct 24 10:18:47 2000 From: simonb@REDACTED (Simon Bennett) Date: Tue, 24 Oct 2000 09:18:47 +0100 Subject: GS application References: <39F5263D.F0C3F504@eede.ericsson.se> Message-ID: <39F545E7.2AC1BCA7@terminus.ericsson.se> Gerald Biederbeck wrote: > I installed GS and Erlang too. > It works fine. > > Priya Paramasevam wrote: > > > I am using Erlang to develop a simulation. I need to use GS for this. I > > have installed Erlang and GS on my computer. Unfortunately, I am not able > > to view the GS window. What I think Gerald is saying is that you have not given people on the list much to go on.... For example: What operating system are you using? What version of Erlang are you using? Is OTP installed as part of the Erlang installation or separately? If the latter, have you installed it? What have you actually done to test it? Run an existing program? Written your own program? Entered commands in the shell? What was the response? Did any error messages appear anywhere? Simon Bennett Information Systems Consultant ___________________________________________________________________ Simon Bennett E-mail: simonb@REDACTED Ericsson Intracom http://www.ericsson.co.uk/UK/intracom 1 Bede Island Road Voice (UK) 0116 2542400 Leicester Voice (int) +44 116 2542400 England Voice ECN: 832 707 ext 232 LE2 7EU Fax: +44 (0)116 2046111 ___________________________________________________________________ From etxuwig@REDACTED Tue Oct 24 11:22:33 2000 From: etxuwig@REDACTED (Ulf Wiger) Date: Tue, 24 Oct 2000 11:22:33 +0200 (MET DST) Subject: non-Erlang Erlang grammars In-Reply-To: Message-ID: On Tue, 24 Oct 2000, Vlad Dumitrescu wrote: >I was thinking about this problem, and I suppose the solution using funs >would be to provide an ets:lookup/3, where the third argument is a fun that >filters the results of ets:lookup/2 and also filters the elements that will >be outputted to the result. Also, list comprehensions that accept an ets >generator would also do fine, even more elegantly than simple funs. > >As I understand it, the big problem is how to make the funs evaluate in ets >space, not local space. But otherwise I can't understand why these funs >should abide by the following rules >>- no message passing >>- no external function calls >>- no loops >>- no operations that are not known to be O(1) > >Is it an efficiency aspect? Or an implementation issue? Or a more >deep-going architecture one? >Is it because the funs woill be evaluated in a different space? >Could someone please explain this? Thanks. > >Vlad It's partly an efficiency issue, I guess. If you want to apply a trace filter, for example, you'll want some assurance that the filter fun is as efficient as it can be. It would also be nice to know that the function is guaranteed to terminate at all. Basically, this borders on another topic that I've thought about from time to time: fast-path processing. This is what would happen for example in a content-based router (or perhaps a web switch, right?) It would be nice if one could write some kind of "erlang lite" code which is perhaps a subset of normal erlang, but has much of the same feel. The main feature of erlang lite would be that it is optimized for fast-path processing (say, able to handle many MBytes or even GBytes per second on reasonable hardware.) Message passing is not an efficiency aspect. It's more about being able to _know_ that the code you're calling is side-effect free, which is something that would often be useful. One example is the ACID property of mnesia transactions, which can't be fully guaranteed if the transaction funs contain side effects. /Uffe -- Ulf Wiger tfn: +46 8 719 81 95 Strategic Product & System Management mob: +46 70 519 81 95 Ericsson Telecom AB, Datacom Networks and IP Services Varuv?gen 9, ?lvsj?, S-126 25 Stockholm, Sweden From Sean.Hinde@REDACTED Tue Oct 24 11:29:09 2000 From: Sean.Hinde@REDACTED (Sean Hinde) Date: Tue, 24 Oct 2000 10:29:09 +0100 Subject: Porting Erlang Message-ID: <402DD461F109D411977E0008C791C312565240@imp02mbx.one2one.co.uk> Mark, > Thanks for the pointers. I'm also evaluating LynxOS > (http://www.lynuxworks.com/products/whatislos.h> tml) which > appears to be much more POSIX/Unix flavored than > VRTX and VxWorks, and fully supports the GNU toolchain. Have you looked at QNX? Erlang basically compiles out of the box on this and it seems to have support for boot flash, memory protection, and your real time stuff. They just released a beta of their latest version at: http://www.qnx.com Rgds, Sean NOTICE AND DISCLAIMER: This email (including attachments) is confidential. If you have received this email in error please notify the sender immediately and delete this email from your system without copying or disseminating it or placing any reliance upon its contents. We cannot accept liability for any breaches of confidence arising through use of email. Any opinions expressed in this email (including attachments) are those of the author and do not necessarily reflect our opinions. We will not accept responsibility for any commitments made by our employees outside the scope of our business. We do not warrant the accuracy or completeness of such information. From Gerald.Biederbeck@REDACTED Tue Oct 24 11:38:50 2000 From: Gerald.Biederbeck@REDACTED (Gerald Biederbeck) Date: Tue, 24 Oct 2000 11:38:50 +0200 Subject: Limitation on number of messages? Message-ID: <39F558AA.C1C29A34@eede.ericsson.se> Hi, i'm going to implement a gen_server. Scenario: Let's say the gen_server process is busy performing a handle_cast/2. Meanwhile I send messages to the server -> the messages are queued! How many Messages will be queued? (number of messages) Is it possible that messages are discarded when the queue is full? What size is the Maximum of the Queue? (Bytes) Are there any limitations at all? (besides memory!) Is this dependent on the OS i'm using? (here: Unix) Are there known problems related to a large/long queue of messages (e.g. Performance)? I found nothing on the OTP-Doc page related to this topic. -> maybe there is no problem at all! Thanx for any hint /Gerry From rv@REDACTED Tue Oct 24 11:49:37 2000 From: rv@REDACTED (Robert Virding) Date: Tue, 24 Oct 2000 11:49:37 +0200 Subject: non-Erlang Erlang grammars In-Reply-To: Your message of "Tue, 24 Oct 2000 08:59:02 +0200." Message-ID: <200010240949.LAA02200@trana.bluetail.com> "Vlad Dumitrescu" writes: >----- Original Message ----- >I was thinking about this problem, and I suppose the solution using funs >would be to provide an ets:lookup/3, where the third argument is a fun that >filters the results of ets:lookup/2 and also filters the elements that will >be outputted to the result. Also, list comprehensions that accept an ets >generator would also do fine, even more elegantly than simple funs. > >As I understand it, the big problem is how to make the funs evaluate in ets >space, not local space. But otherwise I can't understand why these funs >should abide by the following rules >>- no message passing >>- no external function calls >>- no loops >>- no operations that are not known to be O(1) > >Is it an efficiency aspect? Or an implementation issue? Or a more deep-going >architecture one? >Is it because the funs woill be evaluated in a different space? >Could someone please explain this? Thanks. It is basically an issue about efficiency in the current implementation. Today ets tables are stored in a separate heap area outside all processes so data transfer with insert and lookup are done by copying between the ets table area and the process heap. When searching for data you would like to be able to do as much of the selection work as possible without copying any data from the ets area. that is why you have functions like match/match_object which take a pattern, the pattern matching can be done without copying. This also the reason why you have match specs, they can be interpreted by the emulator without copying any data. This also means that functions which fold over tables with general functions will be inherently more inefficient as all the objects must be copied before the fold function can be applied. Therefore if you wish to replace match specs with funs, which would look MUCH better, then you have to be very restrictive, basically what I listed, to get the same efficiency as match specs. Basically coding match specs in the pattern and guard part of fun clauses. fun (#foo_type{a=A,b=B}) when A > B+3 -> true; (#foo_type{c=sune}) -> true; (Other) -> false. The funs would not be evaluating in any space as the ets table areas are very specialised for storing ets tables and not suitable for general term usage. I suppose you could run in normal process space but this would cause some really hairy problems with the collector as you would be adding inter heap references to system which does support it. By checking the legality of the funs at run time you get around the problem of how you would check these funs at compile time. Basically impossible unless you impose severe restriction on how the funs are defined. I would LOVE to be to send over general funs with match/match_object to extract objects from tables but this would mean an almost complete rewrite of the ets table code, which is unrealistic today. Robert -- Robert Virding Tel: +46 (0)8 545 55 017 Alteon Web Systems Email: rv@REDACTED S:t Eriksgatan 44 WWW: http://www.bluetail.com/~rv SE-112 34 Stockholm, SWEDEN "Folk s?ger att jag inte bryr mig om n?gonting, men det skiter jag i". From Peter.Andersson@REDACTED Tue Oct 24 14:18:54 2000 From: Peter.Andersson@REDACTED (Peter Andersson) Date: Tue, 24 Oct 2000 13:18:54 +0100 Subject: Limitation on number of messages? References: <39F558AA.C1C29A34@eede.ericsson.se> Message-ID: <39F57E2E.59850594@eei.ericsson.se> Hi, As far as I know there is no limit other than the system memory (no messages are discarded) and it is not OS dependent. You have to verify yourself that this is not a problem in your application. It can indeed be a problem. I've seen the following a few times: A process has done a gen_server call to another process, which, for some reason, does not reply (in time). Meanwhile the waiting process receives incoming messages and after some time the Erlang node can not allocate more memory for the queue. The result is that the node crashes with a report like "Can not allocate ??? bytes of memory". What you wanted was really an error report stating the reason for the missing reply! It is important you use a timeout value in gen_server calls for making it possible to detect these types of errors rather than getting a node crash. You might be tempted to use the 'infinity' setting for convenience, but be careful then. (Another benefit of using relevant timeout values in calls is that you can detect performance problems in early tests this way, i.e. use a short timer value at first to see if you get the response times you expect, later set it up to a "safe worst-case" value). Regards /Peter Gerald Biederbeck wrote: > > Hi, > > i'm going to implement a gen_server. > Scenario: > Let's say the gen_server process is busy performing a handle_cast/2. > Meanwhile I send messages to the server > -> the messages are queued! > > How many Messages will be queued? (number of messages) > Is it possible that messages are discarded when the queue is full? > What size is the Maximum of the Queue? (Bytes) > > Are there any limitations at all? (besides memory!) > Is this dependent on the OS i'm using? (here: Unix) > Are there known problems related to a large/long queue of messages (e.g. > Performance)? > > I found nothing on the OTP-Doc page related to this topic. > -> maybe there is no problem at all! > > Thanx for any hint > /Gerry From thomas@REDACTED Tue Oct 24 16:02:56 2000 From: thomas@REDACTED (Thomas Arts) Date: Tue, 24 Oct 2000 16:02:56 +0200 Subject: Erlang Consulting/support/training in the US? References: <001a01c03d50$036320c0$8cfea8c0@neomeo.com> Message-ID: <39F59690.3BEFD8BB@cslab.ericsson.se> Alexis L?-Qu?c wrote: > > Greetings, > > I'm looking for people/companies providing consulting and training in the > US. How do I find if there are any? You could contact J?rn Wegener from Erlang systems. /Thomas From sam@REDACTED Tue Oct 24 22:12:46 2000 From: sam@REDACTED (Samuel Tardieu) Date: Tue, 24 Oct 2000 22:12:46 +0200 Subject: Amusing stuff Message-ID: <2000-10-24-22-12-46+trackit+sam@inf.enst.fr> I've played a little with Apache modules today and came out with a small mod_erl.c, that can talk using a very simple protocol to an Erlang node so that Erlang code can be embedded within HTML very easily. I will try to make a clean release as soon as possible, but if you are interested in looking at what it looks like, you can go onto http://www.rfc1149.net/devel/mod_erl.en.eerl It may or may not work (I haven't upgraded Erlang on the machine running the WWW server so it relies on another machine since I use the bit syntax) since I will not be able to check the machines for the next 24 hours, but when it works, it is fun to see 20! computed from within your HTML code. From etxuwig@REDACTED Wed Oct 25 09:49:27 2000 From: etxuwig@REDACTED (Ulf Wiger) Date: Wed, 25 Oct 2000 09:49:27 +0200 (MET DST) Subject: Amusing stuff In-Reply-To: <2000-10-24-22-12-46+trackit+sam@inf.enst.fr> Message-ID: Amusing indeed. I'm eagerly awaiting its release. /Uffe On Tue, 24 Oct 2000, Samuel Tardieu wrote: >I've played a little with Apache modules today and came out with a small >mod_erl.c, that can talk using a very simple protocol to an Erlang node >so that Erlang code can be embedded within HTML very easily. > >I will try to make a clean release as soon as possible, but if you >are interested in looking at what it looks like, you can go onto > > http://www.rfc1149.net/devel/mod_erl.en.eerl > >It may or may not work (I haven't upgraded Erlang on the machine running >the WWW server so it relies on another machine since I use the bit >syntax) since I will not be able to check the machines for the >next 24 hours, but when it works, it is fun to see 20! computed from >within your HTML code. > > -- Ulf Wiger tfn: +46 8 719 81 95 Strategic Product & System Management mob: +46 70 519 81 95 Ericsson Telecom AB, Datacom Networks and IP Services Varuv?gen 9, ?lvsj?, S-126 25 Stockholm, Sweden From pparamas@REDACTED Wed Oct 25 09:52:09 2000 From: pparamas@REDACTED (Priya Paramasevam) Date: Wed, 25 Oct 2000 18:52:09 +1100 (EST) Subject: GS application In-Reply-To: <39F545E7.2AC1BCA7@terminus.ericsson.se> Message-ID: Hello Simon .. Greetings and Good Day, Thank you for replying. Actually, I think I had accidentally sent the mail without Kind Regards, Priya Paramasevam Master of Applied Science (IT) RMIT University, Melbourne, Australia. ========================================================================== A good sense of humour, a big dose of patience, and a dash of humility, you will be rewarded many-fold! ========================================================================== On Tue, 24 Oct 2000, Simon Bennett wrote: > > What I think Gerald is saying is that you have not given people on the > list much to go on.... > > For example: > > What operating system are you using? > > What version of Erlang are you using? > > Is OTP installed as part of the Erlang installation or separately? If > the latter, have you installed it? > > What have you actually done to test it? Run an existing program? > Written your own program? Entered commands in the shell? > > What was the response? Did any error messages appear anywhere? > > Simon Bennett > Information Systems Consultant > ___________________________________________________________________ > > Simon Bennett E-mail: simonb@REDACTED > Ericsson Intracom http://www.ericsson.co.uk/UK/intracom > 1 Bede Island Road Voice (UK) 0116 2542400 > Leicester Voice (int) +44 116 2542400 > England Voice ECN: 832 707 ext 232 > LE2 7EU Fax: +44 (0)116 2046111 > ___________________________________________________________________ > From pparamas@REDACTED Wed Oct 25 09:57:07 2000 From: pparamas@REDACTED (Priya Paramasevam) Date: Wed, 25 Oct 2000 18:57:07 +1100 (EST) Subject: GS application In-Reply-To: <39F545E7.2AC1BCA7@terminus.ericsson.se> Message-ID: hello i did it again .. i accidentally sent the mail without completing it. i have compiled and run some of the example programs provided together with GS. they run fine. but when i test a simple program that i wrote myself, i can't seem to see the output. here is my code: -module(line2). -export([start/0, internal/0]). start() -> spawn(line2, internal, []). internal() -> I = gs:start(), Win = gs:window(I, [{title, "Demo"}, {width, 200}, {height, 270}]), Path = [{10,50}, {30, 50}, {30, 60}, {60, 60}], gs:draw_line(Win, solid, 2, Path), gs:config(Win, {map, true}). there isn't any problem compiling, but there is no output. can you please tell me what is wrong? thank you so much and sorry for the redundant meaningless mails. Kind Regards, Priya Paramasevam Master of Applied Science (IT) RMIT University, Melbourne, Australia. ========================================================================== A good sense of humour, a big dose of patience, and a dash of humility, you will be rewarded many-fold! ========================================================================== On Tue, 24 Oct 2000, Simon Bennett wrote: > Gerald Biederbeck wrote: > > > I installed GS and Erlang too. > > It works fine. > > > > Priya Paramasevam wrote: > > > > > I am using Erlang to develop a simulation. I need to use GS for this. I > > > have installed Erlang and GS on my computer. Unfortunately, I am not able > > > to view the GS window. > > What I think Gerald is saying is that you have not given people on the > list much to go on.... > > For example: > > What operating system are you using? > > What version of Erlang are you using? > > Is OTP installed as part of the Erlang installation or separately? If > the latter, have you installed it? > > What have you actually done to test it? Run an existing program? > Written your own program? Entered commands in the shell? > > What was the response? Did any error messages appear anywhere? > > Simon Bennett > Information Systems Consultant > ___________________________________________________________________ > > Simon Bennett E-mail: simonb@REDACTED > Ericsson Intracom http://www.ericsson.co.uk/UK/intracom > 1 Bede Island Road Voice (UK) 0116 2542400 > Leicester Voice (int) +44 116 2542400 > England Voice ECN: 832 707 ext 232 > LE2 7EU Fax: +44 (0)116 2046111 > ___________________________________________________________________ > From cesarini@REDACTED Wed Oct 25 10:39:37 2000 From: cesarini@REDACTED (Francesco Cesarini) Date: Wed, 25 Oct 2000 09:39:37 +0100 Subject: GS application References: Message-ID: <39F69C49.B079ADE7@terminus.ericsson.se> Hi Priya, you have an error in your code. What happens is that a new process is spawned, but it crashes when executing . gs:draw_line(Win, solid, 2, Path) Which does not exist. The error is not detected at compile time because Erlang modules are compiled on a stand alone basis and not linked to each other. You can easily find the error by calling internal/0 directly from the shell 6> line2:internal(). ** exited: {undef,{gs,draw_line, [{5,<0.45.0>},solid,2,[{10,50},{30,50},{30,60},{60,60}]]}} ** 7> Other errors include the fact that line takes a canvas as a parent and not a Window. Look at the online documentation @ http://www.erlang.org/doc/current/lib/gs-1.3.7/doc/index.html for exact parameters to pass to the function. Regards, Francesco -- Francesco Cesarini Erlang/OTP consultant Cellular: INT+44-7776 250381 ECN: 832-707192 http://welcome.to/cesarini.consulting Priya Paramasevam wrote: > > hello > > i did it again .. i accidentally sent the mail without completing it. > i have compiled and run some of the example programs provided together > with GS. they run fine. but when i test a simple program that i wrote > myself, i can't seem to see the output. > > here is my code: > > -module(line2). > -export([start/0, internal/0]). > > start() -> > spawn(line2, internal, []). > > internal() -> > I = gs:start(), > Win = gs:window(I, [{title, "Demo"}, {width, 200}, {height, > 270}]), > Path = [{10,50}, {30, 50}, {30, 60}, {60, 60}], > gs:draw_line(Win, solid, 2, Path), > gs:config(Win, {map, true}). > > there isn't any problem compiling, but there is no output. can you please > tell me what is wrong? > > thank you so much and sorry for the redundant meaningless mails. > > Kind Regards, > Priya Paramasevam > Master of Applied Science (IT) > RMIT University, > Melbourne, Australia. > > > ========================================================================== > A good sense of humour, a big dose of patience, and a dash of humility, > you will be rewarded many-fold! > ========================================================================== > > On Tue, 24 Oct 2000, Simon Bennett wrote: > > > Gerald Biederbeck wrote: > > > > > I installed GS and Erlang too. > > > It works fine. > > > > > > Priya Paramasevam wrote: > > > > > > > I am using Erlang to develop a simulation. I need to use GS for this. I > > > > have installed Erlang and GS on my computer. Unfortunately, I am not able > > > > to view the GS window. > > > > What I think Gerald is saying is that you have not given people on the > > list much to go on.... > > > > For example: > > > > What operating system are you using? > > > > What version of Erlang are you using? > > > > Is OTP installed as part of the Erlang installation or separately? If > > the latter, have you installed it? > > > > What have you actually done to test it? Run an existing program? > > Written your own program? Entered commands in the shell? > > > > What was the response? Did any error messages appear anywhere? > > > > Simon Bennett > > Information Systems Consultant > > ___________________________________________________________________ > > > > Simon Bennett E-mail: simonb@REDACTED > > Ericsson Intracom http://www.ericsson.co.uk/UK/intracom > > 1 Bede Island Road Voice (UK) 0116 2542400 > > Leicester Voice (int) +44 116 2542400 > > England Voice ECN: 832 707 ext 232 > > LE2 7EU Fax: +44 (0)116 2046111 > > ___________________________________________________________________ > > -- Francesco Cesarini Erlang/OTP consultant Cellular: INT+44-7776 250381 ECN: 832-707192 From simonb@REDACTED Wed Oct 25 10:50:52 2000 From: simonb@REDACTED (Simon Bennett) Date: Wed, 25 Oct 2000 09:50:52 +0100 Subject: GS application References: Message-ID: <39F69EEC.27F49106@terminus.ericsson.se> Hi Firstly, the process is terminating as soon as it has run your internal() function, so you don't get to see any output. Change the last line to end with a comma and add a line on the end like: loop(Win). Then add a function that waits for an event. The simplest is something like: loop(Win) -> receive {gs,Win,destroy,_Data,_Arg} -> bye end. However, you want it to respond to events, handle them and carry on, so you will need to write additional receive clauses which do what you want and then call loop(Win) again. Secondly, there is a problem with your draw_line statement. Comment it out and you'll see the window. One of the problems with GS is getting at error information from the TK back-end. Generally, if you get the syntax wrong, you get nothing. I think that to draw a line you need a canvas. You then need to use gs:line(...) or gs:create(line,...) and pass that the variable holding the reference to the canvas. draw_line looks like it might be a function that someone has written, but I don't think it's part of GS. Good luck Simon Priya Paramasevam wrote: > > hello > > i did it again .. i accidentally sent the mail without completing it. > i have compiled and run some of the example programs provided together > with GS. they run fine. but when i test a simple program that i wrote > myself, i can't seem to see the output. > > here is my code: > > -module(line2). > -export([start/0, internal/0]). > > start() -> > spawn(line2, internal, []). > > internal() -> > I = gs:start(), > Win = gs:window(I, [{title, "Demo"}, {width, 200}, {height, > 270}]), > Path = [{10,50}, {30, 50}, {30, 60}, {60, 60}], > gs:draw_line(Win, solid, 2, Path), > gs:config(Win, {map, true}). > > there isn't any problem compiling, but there is no output. can you please > tell me what is wrong? > > thank you so much and sorry for the redundant meaningless mails. > > Kind Regards, > Priya Paramasevam > Master of Applied Science (IT) > RMIT University, > Melbourne, Australia. > > > ========================================================================== > A good sense of humour, a big dose of patience, and a dash of humility, > you will be rewarded many-fold! > ========================================================================== > > On Tue, 24 Oct 2000, Simon Bennett wrote: > > > Gerald Biederbeck wrote: > > > > > I installed GS and Erlang too. > > > It works fine. > > > > > > Priya Paramasevam wrote: > > > > > > > I am using Erlang to develop a simulation. I need to use GS for this. I > > > > have installed Erlang and GS on my computer. Unfortunately, I am not able > > > > to view the GS window. > > > > What I think Gerald is saying is that you have not given people on the > > list much to go on.... > > > > For example: > > > > What operating system are you using? > > > > What version of Erlang are you using? > > > > Is OTP installed as part of the Erlang installation or separately? If > > the latter, have you installed it? > > > > What have you actually done to test it? Run an existing program? > > Written your own program? Entered commands in the shell? > > > > What was the response? Did any error messages appear anywhere? > > > > Simon Bennett > > Information Systems Consultant > > ___________________________________________________________________ > > > > Simon Bennett E-mail: simonb@REDACTED > > Ericsson Intracom http://www.ericsson.co.uk/UK/intracom > > 1 Bede Island Road Voice (UK) 0116 2542400 > > Leicester Voice (int) +44 116 2542400 > > England Voice ECN: 832 707 ext 232 > > LE2 7EU Fax: +44 (0)116 2046111 > > ___________________________________________________________________ > > -- Simon Bennett Information Systems Consultant ___________________________________________________________________ Simon Bennett E-mail: simonb@REDACTED Ericsson Intracom http://www.ericsson.co.uk/UK/intracom 1 Bede Island Road Voice (UK) 0116 2542400 Leicester Voice (int) +44 116 2542400 England Voice ECN: 832 707 ext 232 LE2 7EU Fax: +44 (0)116 2046111 ___________________________________________________________________ From tonyp@REDACTED Wed Oct 25 10:51:29 2000 From: tonyp@REDACTED (Tony Pedley) Date: Wed, 25 Oct 2000 09:51:29 +0100 Subject: GS application References: <39F69C49.B079ADE7@terminus.ericsson.se> Message-ID: <39F69F11.90728FB7@terminus.ericsson.se> Try this instead -module(line2). -export([start/0, internal/0]). start() -> spawn(line2, internal, []). internal() -> I = gs:start(), Win = gs:window(I, [{map,true},{title, "Demo"}, {width, 200}, {height,270}]), Path = [{10,50}, {30, 50}, {30, 60}, {60, 60}], Canvas = gs:canvas(Win,[]), gs:line(Canvas,[{width,2}, {coords,Path}]), gs:config(Win, {map, true}). loop(). loop() -> receive Any -> ok end, loop(). To add to Francesco comments You must have some sort of loop at the end to ensure the process will not end prematurely Otherwise your window will be destroyed. All the best Tony Francesco Cesarini wrote: > Hi Priya, > you have an error in your code. What happens is that a new process is > spawned, but it crashes when executing . > > gs:draw_line(Win, solid, 2, Path) > > Which does not exist. > > The error is not detected at compile time because Erlang modules are > compiled on a stand alone basis and not linked to each other. > > You can easily find the error by calling internal/0 directly from the > shell > 6> line2:internal(). > ** exited: {undef,{gs,draw_line, > > [{5,<0.45.0>},solid,2,[{10,50},{30,50},{30,60},{60,60}]]}} ** > 7> > > Other errors include the fact that line takes a canvas as a parent and > not a Window. Look at the online documentation @ > http://www.erlang.org/doc/current/lib/gs-1.3.7/doc/index.html for exact > parameters to pass to the function. > > Regards, > Francesco > -- > Francesco Cesarini > > Erlang/OTP consultant > Cellular: INT+44-7776 250381 > ECN: 832-707192 > http://welcome.to/cesarini.consulting > > Priya Paramasevam wrote: > > > > hello > > > > i did it again .. i accidentally sent the mail without completing it. > > i have compiled and run some of the example programs provided together > > with GS. they run fine. but when i test a simple program that i wrote > > myself, i can't seem to see the output. > > > > here is my code: > > > > -module(line2). > > -export([start/0, internal/0]). > > > > start() -> > > spawn(line2, internal, []). > > > > internal() -> > > I = gs:start(), > > Win = gs:window(I, [{title, "Demo"}, {width, 200}, {height, > > 270}]), > > Path = [{10,50}, {30, 50}, {30, 60}, {60, 60}], > > gs:draw_line(Win, solid, 2, Path), > > gs:config(Win, {map, true}). > > > > there isn't any problem compiling, but there is no output. can you please > > tell me what is wrong? > > > > thank you so much and sorry for the redundant meaningless mails. > > > > Kind Regards, > > Priya Paramasevam > > Master of Applied Science (IT) > > RMIT University, > > Melbourne, Australia. > > > > > > ========================================================================== > > A good sense of humour, a big dose of patience, and a dash of humility, > > you will be rewarded many-fold! > > ========================================================================== > > > > On Tue, 24 Oct 2000, Simon Bennett wrote: > > > > > Gerald Biederbeck wrote: > > > > > > > I installed GS and Erlang too. > > > > It works fine. > > > > > > > > Priya Paramasevam wrote: > > > > > > > > > I am using Erlang to develop a simulation. I need to use GS for this. I > > > > > have installed Erlang and GS on my computer. Unfortunately, I am not able > > > > > to view the GS window. > > > > > > What I think Gerald is saying is that you have not given people on the > > > list much to go on.... > > > > > > For example: > > > > > > What operating system are you using? > > > > > > What version of Erlang are you using? > > > > > > Is OTP installed as part of the Erlang installation or separately? If > > > the latter, have you installed it? > > > > > > What have you actually done to test it? Run an existing program? > > > Written your own program? Entered commands in the shell? > > > > > > What was the response? Did any error messages appear anywhere? > > > > > > Simon Bennett > > > Information Systems Consultant > > > ___________________________________________________________________ > > > > > > Simon Bennett E-mail: simonb@REDACTED > > > Ericsson Intracom http://www.ericsson.co.uk/UK/intracom > > > 1 Bede Island Road Voice (UK) 0116 2542400 > > > Leicester Voice (int) +44 116 2542400 > > > England Voice ECN: 832 707 ext 232 > > > LE2 7EU Fax: +44 (0)116 2046111 > > > ___________________________________________________________________ > > > > > -- > Francesco Cesarini > > Erlang/OTP consultant > Cellular: INT+44-7776 250381 > ECN: 832-707192 -- ______________________________________________________________________ Tony Pedley mailto:tonyp@REDACTED Ericsson Intracom Ltd. Intranet : http://intracom.ericsson.se 1 Bede Island Internet : http://www.ericsson.co.uk/datacom/index.htm Leicester memoID : ECOM.CBERAM England Tel : +44 (0)116 2 542 400 LE2 7EU Fax : +44 (0)116 2 046 111 ______________________________________________________________________ -------------- next part -------------- An HTML attachment was scrubbed... URL: From gulias@REDACTED Wed Oct 25 11:36:58 2000 From: gulias@REDACTED (Victor M. Gulias) Date: 25 Oct 2000 11:36:58 +0200 Subject: ASN.1 questions Message-ID: A couple of questions about the ASN.1 compiler. Could some of you tell me why the following definition (from the SET specification) does not compile? --------------- P2 DEFINITIONS EXPLICIT TAGS ::= BEGIN SETString { INTEGER:10 } ::= CHOICE { visibleString VisibleString (SIZE(1..10)), bmpString BMPString (SIZE(1..10)) } END ---------------- % erlc -bber P2.asn Erlang ASN.1 version "1.2.7" compiling "/home/lfcia/gulias/work/set/ejemplos/SET/1/P2.asn" Compiler Options: [fast, ber, report_errors, {cwd,'/home/lfcia/gulias/work/set/ejemplos/SET/1'}, {outdir,"/home/lfcia/gulias/work/set/ejemplos/SET/1"}] "syntax error before: " ["'INTEGER'"] at line 6 Compiler function asn1ct:compile_asn/3 returned: {error,{6,["syntax error before: ",["'INTEGER'"]]}} make: *** [all] Error 1 ----------------- If we remove the { INTEGER:10 } from the LHS of the definition it seems to work fine. Also, the ASN.1 compiler uses per and ber to code the types. What about *der* encoding? Regards, -- Victor M. Gulias From klacke@REDACTED Wed Oct 25 11:37:52 2000 From: klacke@REDACTED (Klacke) Date: Wed, 25 Oct 2000 11:37:52 +0200 Subject: GS application In-Reply-To: <39F69C49.B079ADE7@terminus.ericsson.se> References: <39F69C49.B079ADE7@terminus.ericsson.se> Message-ID: <20001025113752.A18787@bluetail.com> > you have an error in your code. What happens is that a new process is > spawned, but it crashes when executing . > > gs:draw_line(Win, solid, 2, Path) > > Which does not exist. > > The error is not detected at compile time because Erlang modules are > compiled on a stand alone basis and not linked to each other. > > You can easily find the error by calling internal/0 directly from the > shell > 6> line2:internal(). > ** exited: {undef,{gs,draw_line, It can be a bit confusing for newbies with processes that silently crash. If we first start sasl ( application:start(sasl) ), and then instead of spawn call, proc_lib:spaw/3, we get a nice error printout on the tty. =CRASH REPORT==== 25-Oct-2000::11:33:59 === crasher: pid: <0.61.0> registered_name: [] error_info: {undef,[{gs,draw_line, [{7,<0.31.0>}, solid, 2, [{10,50},{30,50},{30,60},{60,60}]]}, {line2,internal,0}, {proc_lib,init_p,5}]} initial_call: {line2,internal,[]} ancestors: [<0.22.0>] messages: [] links: [<0.31.0>] dictionary: [] trap_exit: false status: running heap_size: 377 stack_size: 21 reductions: 120 neighbours: Then it's pretty clear what goes on. /klacke From etxuwig@REDACTED Wed Oct 25 11:43:38 2000 From: etxuwig@REDACTED (Ulf Wiger) Date: Wed, 25 Oct 2000 11:43:38 +0200 (MET DST) Subject: GS application In-Reply-To: <20001025113752.A18787@bluetail.com> Message-ID: You can do this quite easily by using "erl -boot start_sasl", or even set this as the default boot script when installing Erlang. If you build your own boot scripts, all you have to do is of course to add sasl in the list of applications in your .rel file. /Uffe On Wed, 25 Oct 2000, Klacke wrote: >It can be a bit confusing for newbies with processes that silently crash. >If we first start sasl ( application:start(sasl) ), and then instead >of spawn call, proc_lib:spaw/3, we get a nice error printout on the tty. > >=CRASH REPORT==== 25-Oct-2000::11:33:59 === > crasher: > pid: <0.61.0> > registered_name: [] > error_info: {undef,[{gs,draw_line, > [{7,<0.31.0>}, > solid, > 2, > [{10,50},{30,50},{30,60},{60,60}]]}, > {line2,internal,0}, > {proc_lib,init_p,5}]} > initial_call: {line2,internal,[]} > ancestors: [<0.22.0>] > messages: [] > links: [<0.31.0>] > dictionary: [] > trap_exit: false > status: running > heap_size: 377 > stack_size: 21 > reductions: 120 > neighbours: > > >Then it's pretty clear what goes on. > >/klacke -- Ulf Wiger tfn: +46 8 719 81 95 Strategic Product & System Management mob: +46 70 519 81 95 Ericsson Telecom AB, Datacom Networks and IP Services Varuv?gen 9, ?lvsj?, S-126 25 Stockholm, Sweden From mbj@REDACTED Wed Oct 25 12:51:36 2000 From: mbj@REDACTED (Martin Bjorklund) Date: Wed, 25 Oct 2000 12:51:36 +0200 Subject: GS application In-Reply-To: Your message of "Wed, 25 Oct 2000 11:43:38 +0200 (MET DST)" References: Message-ID: <20001025125136J.mbj@bluetail.com> Ulf Wiger wrote: > > You can do this quite easily by using "erl -boot start_sasl", or even > set this as the default boot script when installing Erlang. Unfortunately, you will also get Progress Reports, which during normal development tend to be quite verbose... You can suppress them by starting erlang as: erl -boot start_sasl -sasl errlog_type error (This should probably have been the default.) /martin From matthias@REDACTED Wed Oct 25 12:55:18 2000 From: matthias@REDACTED (matthias@REDACTED) Date: Wed, 25 Oct 2000 12:55:18 +0200 (CEST) Subject: GS application In-Reply-To: References: <39F545E7.2AC1BCA7@terminus.ericsson.se> Message-ID: <14838.48150.531728.85552@corelatus.com> Hi, You might want to have a look at the example code which comes with the library and modify that one step at a time. You're hitting a couple of problems: 1. There is no function called gs:draw_line. (What made you think there was?) 2. Your program spawns a process which creates a window and then dies. Since your process died, everything you did in gs dies too. (3. You spawn the process without linking, so you never get notice of it dying.) Here's a program which does what I think you're trying to do: -module(line2). -export([start/0, internal/0]). start() -> spawn_link(line2, internal, []). internal() -> I = gs:start(), Win = gs:window(I, [{title, "Working demo"}, {width, 200}, {height, 270}]), Path = [{10,50}, {30, 50}, {30, 60}, {60, 60}], gs:config(Win, {map, true}), %% display the window for 10 seconds and then die timer:sleep(10000). You might want to ask someone local (Maurice Castro?) to help you get started on trying out code using the erlang shell. Your program is a perfect example of something which is easily debugged using the shell. Matthias From Sean.Hinde@REDACTED Wed Oct 25 13:40:18 2000 From: Sean.Hinde@REDACTED (Sean Hinde) Date: Wed, 25 Oct 2000 12:40:18 +0100 Subject: Starting erl using sh -c Message-ID: <402DD461F109D411977E0008C791C31256524A@imp02mbx.one2one.co.uk> I'm trying to work around a QNX problem with erlc. erlc starts the emulator using the execvp() call which is broken in QNX unless the command is prefixed with sh -c >From the UNIX prompt "sh -c erl -noinput -s erl_compile compile ...etc" just starts a straight emulator - the erl shell script seems to lose everything after the erl. My UNIX shell knowledge runs out at this point. Anyone any ideas? Thanks, Sean NOTICE AND DISCLAIMER: This email (including attachments) is confidential. If you have received this email in error please notify the sender immediately and delete this email from your system without copying or disseminating it or placing any reliance upon its contents. We cannot accept liability for any breaches of confidence arising through use of email. Any opinions expressed in this email (including attachments) are those of the author and do not necessarily reflect our opinions. We will not accept responsibility for any commitments made by our employees outside the scope of our business. We do not warrant the accuracy or completeness of such information. From klacke@REDACTED Wed Oct 25 13:49:07 2000 From: klacke@REDACTED (Klacke) Date: Wed, 25 Oct 2000 13:49:07 +0200 Subject: ASN.1 questions In-Reply-To: References: Message-ID: <20001025134907.C18787@bluetail.com> On Wed, Oct 25, 2000 at 11:36:58AM +0200, Victor M. Gulias wrote: > > A couple of questions about the ASN.1 compiler. Could some of you tell me why > the following definition (from the SET specification) does not compile? > > --------------- > P2 > DEFINITIONS EXPLICIT TAGS ::= > BEGIN > > > SETString { INTEGER:10 } ::= CHOICE { > visibleString VisibleString (SIZE(1..10)), > bmpString BMPString (SIZE(1..10)) > } > So what does the "{ INTEGER:10 }" construct mean. I've never seen that before. Maybe some recent addition to the standard ?? /klacke -- Claes Wikstrom Bluetail AB http://www.bluetail.com From simonb@REDACTED Wed Oct 25 13:55:36 2000 From: simonb@REDACTED (Simon Bennett) Date: Wed, 25 Oct 2000 12:55:36 +0100 Subject: Starting erl using sh -c References: <402DD461F109D411977E0008C791C31256524A@imp02mbx.one2one.co.uk> Message-ID: <39F6CA38.BC669861@terminus.ericsson.se> You could try quoting the part of the line that you want sh to run, e.g. sh -c 'erl -noinput -s erl_compile etc ' Simon Sean Hinde wrote: > > > I'm trying to work around a QNX problem with erlc. > > erlc starts the emulator using the execvp() call which is broken > in QNX unless the command is prefixed with sh -c > > > >From the UNIX prompt "sh -c erl -noinput -s erl_compile compile ...etc" just > starts a straight emulator - the erl shell script seems to lose everything > after the erl. > > My UNIX shell knowledge runs out at this point. Anyone any ideas? From vladdu@REDACTED Wed Oct 25 14:12:39 2000 From: vladdu@REDACTED (Vlad Dumitrescu) Date: Wed, 25 Oct 2000 14:12:39 +0200 Subject: Starting erl using sh -c References: <402DD461F109D411977E0008C791C31256524A@imp02mbx.one2one.co.uk> Message-ID: > From the UNIX prompt "sh -c erl -noinput -s erl_compile compile ...etc" just > starts a straight emulator - the erl shell script seems to lose everything > after the erl. I think the command line must be sh -c "erl -noinput -s erl_compile compile ...etc" (with the quotes) but am not 100% sure...- Vlad From daniel.neri@REDACTED Wed Oct 25 14:13:54 2000 From: daniel.neri@REDACTED (Daniel Neri) Date: 25 Oct 2000 14:13:54 +0200 Subject: Starting erl using sh -c In-Reply-To: Sean Hinde's message of "Wed, 25 Oct 2000 12:40:18 +0100" References: <402DD461F109D411977E0008C791C31256524A@imp02mbx.one2one.co.uk> Message-ID: Sean Hinde writes: > erlc starts the emulator using the execvp() call which is broken > in QNX unless the command is prefixed with sh -c Broken how? Is this a documented incompatibility? > From the UNIX prompt "sh -c erl -noinput -s erl_compile compile > ...etc" just starts a straight emulator - the erl shell script seems > to lose everything after the erl. You need to quote the command string (since it contains whitespace): $ sh -c 'erl -noinput -s erl_compile compile ...' Regards, --Daniel -- Daniel Neri mailto:dn@REDACTED Sigicom AB, Sweden http://www.sigicom.com From Sean.Hinde@REDACTED Wed Oct 25 14:20:04 2000 From: Sean.Hinde@REDACTED (Sean Hinde) Date: Wed, 25 Oct 2000 13:20:04 +0100 Subject: Starting erl using sh -c Message-ID: <402DD461F109D411977E0008C791C31256524E@imp02mbx.one2one.co.uk> > Broken how? Is this a documented incompatibility? Broken as in a bug. The documentation is very explicit that it should work.. QNX say it will be fixed in the next beta release ;) Sean NOTICE AND DISCLAIMER: This email (including attachments) is confidential. If you have received this email in error please notify the sender immediately and delete this email from your system without copying or disseminating it or placing any reliance upon its contents. We cannot accept liability for any breaches of confidence arising through use of email. Any opinions expressed in this email (including attachments) are those of the author and do not necessarily reflect our opinions. We will not accept responsibility for any commitments made by our employees outside the scope of our business. We do not warrant the accuracy or completeness of such information. From Sean.Hinde@REDACTED Wed Oct 25 14:22:03 2000 From: Sean.Hinde@REDACTED (Sean Hinde) Date: Wed, 25 Oct 2000 13:22:03 +0100 Subject: Starting erl using sh -c Message-ID: <402DD461F109D411977E0008C791C31256524F@imp02mbx.one2one.co.uk> > You could try quoting the part of the line that you want sh > to run, e.g. > > sh -c 'erl -noinput -s erl_compile etc ' > OK that does it. Now I need to figure out how to make erlc.c add in the quotes. Here goes.. Sean NOTICE AND DISCLAIMER: This email (including attachments) is confidential. If you have received this email in error please notify the sender immediately and delete this email from your system without copying or disseminating it or placing any reliance upon its contents. We cannot accept liability for any breaches of confidence arising through use of email. Any opinions expressed in this email (including attachments) are those of the author and do not necessarily reflect our opinions. We will not accept responsibility for any commitments made by our employees outside the scope of our business. We do not warrant the accuracy or completeness of such information. From kenneth@REDACTED Wed Oct 25 14:59:59 2000 From: kenneth@REDACTED (Kenneth Lundin) Date: Wed, 25 Oct 2000 14:59:59 +0200 Subject: ASN.1 questions References: Message-ID: <39F6D94F.3B46F465@erix.ericsson.se> "Victor M. Gulias" wrote: > > A couple of questions about the ASN.1 compiler. Could some of you tell me why > the following definition (from the SET specification) does not compile? > > --------------- > P2 > DEFINITIONS EXPLICIT TAGS ::= > BEGIN > > SETString { INTEGER:10 } ::= CHOICE { > visibleString VisibleString (SIZE(1..10)), > bmpString BMPString (SIZE(1..10)) > } > > END > ---------------- > > % erlc -bber P2.asn > Erlang ASN.1 version "1.2.7" compiling "/home/lfcia/gulias/work/set/ejemplos/SET/1/P2.asn" > Compiler Options: [fast, > ber, > report_errors, > {cwd,'/home/lfcia/gulias/work/set/ejemplos/SET/1'}, > {outdir,"/home/lfcia/gulias/work/set/ejemplos/SET/1"}] > "syntax error before: " ["'INTEGER'"] at line 6 > Compiler function asn1ct:compile_asn/3 returned: > {error,{6,["syntax error before: ",["'INTEGER'"]]}} > make: *** [all] Error 1 > The { INTEGER:10 } belongs to the X.683 "Parameterization of ASN.1 specifications" which very poorly supported by the current version of the Erlang/ASN.1 compiler. Without having the complete X.683 standard in fresh mind I still wonder if your example is meaningful. When it comes to parameterization you either define a parameterized type with names for the formal parameters or you instantiate an already defined parameteized type by providing the actual parameters with Typenames and or values. In your case it is a definition of a parameterized type. Can you explain what the {INTGER:10} argument is supposed to mean or what effect it is supposed to have. > ----------------- > > If we remove the { INTEGER:10 } from the LHS of the definition it > seems to work fine. Yes since then it is a normal type definition. > > Also, the ASN.1 compiler uses per and ber to code the types. What > about *der* encoding? DER is the same as BER but with some restrictions. The Erlang/ASN.1 compiler does not follow the DER rules 100% but I don't think it matters in practical use. DER puts restrictions on: Length forms (always use definite length) , ok String encoding, ok Set components, no not always Boolean TRUE with all eigth bits set, yes Unused bits in BIT STRING, yes Real values, no we hardly support them at all GeneralString values, no Set and Sequence components with default value, no not always GeneralizedTime, no UTCTime, no For all practical uses that I have seen so far the "no's" in the list are not important. But if someone can show practical examples where it is important please show me and I will implement 100% DER. /Regards Kenneth > > Regards, > > -- > Victor M. Gulias -- Kenneth Lundin Ericsson Utvecklings AB kenneth@REDACTED ?T2/UAB/F/P BOX 1505 +46 8 727 57 25 125 25 ?lvsj? From Peter.Andersson@REDACTED Wed Oct 25 16:40:03 2000 From: Peter.Andersson@REDACTED (Peter Andersson) Date: Wed, 25 Oct 2000 15:40:03 +0100 Subject: IMAP client Message-ID: <39F6F0C3.692DDBFA@eei.ericsson.se> Hi, Is there an open source IMAP client implementation in Erlang somewhere out there? /Peter From Sean.Hinde@REDACTED Wed Oct 25 18:20:21 2000 From: Sean.Hinde@REDACTED (Sean Hinde) Date: Wed, 25 Oct 2000 17:20:21 +0100 Subject: Starting erl using sh -c Message-ID: <402DD461F109D411977E0008C791C312565259@imp02mbx.one2one.co.uk> > OK that does it. Now I need to figure out how to make erlc.c > add in the > quotes. Here goes.. > Hmm, this turned out not to be so easy. With much help from Simon though (thanks very much) the libraries are now compiling under QNX. I'll post all that needs to be done when it gets to the end and I've done some basic testing. Sean NOTICE AND DISCLAIMER: This email (including attachments) is confidential. If you have received this email in error please notify the sender immediately and delete this email from your system without copying or disseminating it or placing any reliance upon its contents. We cannot accept liability for any breaches of confidence arising through use of email. Any opinions expressed in this email (including attachments) are those of the author and do not necessarily reflect our opinions. We will not accept responsibility for any commitments made by our employees outside the scope of our business. We do not warrant the accuracy or completeness of such information. From sam@REDACTED Thu Oct 26 13:24:43 2000 From: sam@REDACTED (Samuel Tardieu) Date: Thu, 26 Oct 2000 13:24:43 +0200 Subject: Amusing stuff In-Reply-To: <2000-10-24-22-12-46+trackit+sam@inf.enst.fr>; from sam@inf.enst.fr on Tue, Oct 24, 2000 at 10:12:46PM +0200 References: <2000-10-24-22-12-46+trackit+sam@inf.enst.fr> Message-ID: <2000-10-26-13-24-43+trackit+sam@inf.enst.fr> On 24/10, Samuel Tardieu wrote: | I will try to make a clean release as soon as possible, but if you | are interested in looking at what it looks like, you can go onto | | http://www.rfc1149.net/devel/mod_erl.en.eerl I've made a preliminary package at this address, if people are interested. Everything is under the BSD license. From alexis@REDACTED Thu Oct 26 22:32:41 2000 From: alexis@REDACTED (=?iso-8859-1?Q?Alexis_L=EA-Qu=F4c?=) Date: Thu, 26 Oct 2000 16:32:41 -0400 Subject: www.eddieware.org is still down. Any idea why? Message-ID: Greetings, I seem to remember that it was a network configuration problem. In the meantime, are there any mirrors of eddie's source code around? -- Alexis L?-Qu?c alexis@REDACTED http://www.neomeo.com -------------- next part -------------- An HTML attachment was scrubbed... URL: From Lon.Willett@REDACTED Thu Oct 26 19:07:28 2000 From: Lon.Willett@REDACTED (Lon Willett) Date: Thu, 26 Oct 2000 18:07:28 +0100 Subject: ASN.1 questions In-Reply-To: <39F6D94F.3B46F465@erix.ericsson.se> References: Message-ID: <5.0.0.25.0.20001025172955.04b98c50@mail0.sse.ie> At 13:59 25/10/00, Kenneth Lundin wrote: >"Victor M. Gulias" wrote: [snip] >> Also, the ASN.1 compiler uses per and ber to code the types. What >> about *der* encoding? >DER is the same as BER but with some restrictions. The >Erlang/ASN.1 compiler does not follow the DER rules 100% but >I don't think it matters in practical use. > >DER puts restrictions on: > >Length forms (always use definite length) , ok >String encoding, ok >Set components, no not always >Boolean TRUE with all eigth bits set, yes >Unused bits in BIT STRING, yes >Real values, no we hardly support them at all >GeneralString values, no >Set and Sequence components with default value, no not always >GeneralizedTime, no >UTCTime, no > >For all practical uses that I have seen so far the "no's" in the list >are not important. But if someone can show practical examples where >it is important please show me and I will implement 100% DER. Coming from the PKI world, I'll point out that the X.500/X.509 does require a bit more in the way of DER encoding. I was thinking about maybe hacking it in myself, but I'm a relative new-comer to Erlang, and haven't yet had a chance to really get into the gory details of the language or the ASN.1 support. To be more specific about what would be useful for X.509 PKI support, and note a few other things about DER: Length encoding -- the restrictions are a bit stronger than what you stated. IIRC, it boils down to having to use the shortest possible encoding of the length (which may be what you are already doing; I haven't checked). UTCTime and GeneralizedTime -- used frequently, and often done wrong (a common source of bugs). SET OF -- used in a couple of places, but the main one of concern is that X.500 names are basically a SEQUENCE OF SET OF AttributeValueAssertion. Usually, the set has only a single element, so there is no problem, but it is not unknown for it to have more. enumerated BIT STRING -- this is distinct from a definite length BIT STRING in that _all_ trailing zero bits should be stripped, whether they are defined or not. This is widely used. defaulted component values -- this is used, but can be worked around by defining them as optional instead, and then removing/filling-in the default outside of the en/de-coder. If I get the time to play with this, I'll be sure to check with you first, so we don't duplicate each others work. But I probably won't have the time this month anyway. )-: Cheers, Lon Willett >/Regards Kenneth >> >> Regards, >> >> -- >> Victor M. Gulias From tobbe@REDACTED Fri Oct 27 09:26:10 2000 From: tobbe@REDACTED (Torbjorn Tornkvist) Date: 27 Oct 2000 09:26:10 +0200 Subject: www.eddieware.org is still down. Any idea why? In-Reply-To: Alexis Lê-Quôc's message of "Thu, 26 Oct 2000 16:32:41 -0400" References: Message-ID: The eddieware site has been brought down apparently. But Geoff Wong has setup eddie at sourceforge.net, go there and search for eddie. Cheers /Tobbe From tobbe@REDACTED Fri Oct 27 09:41:57 2000 From: tobbe@REDACTED (Torbjorn Tornkvist) Date: 27 Oct 2000 09:41:57 +0200 Subject: ASN.1 questions In-Reply-To: Lon Willett's message of "Thu, 26 Oct 2000 18:07:28 +0100" References: <5.0.0.25.0.20001025172955.04b98c50@mail0.sse.ie> Message-ID: Hi ! > Coming from the PKI world, I'll point out that the X.500/X.509 > does require a bit more in the way of DER encoding. Regarding PKI, I'm a bit curious, does this also apply for LDAP ? I've just been reading RFC 2259 and 1778 and as I understand it the relevant LDAP attributes should be encoded according to the 'Undefined' syntax (RFC 1778, chap.2.1), which states that it should be BER encoded. > If I get the time to play with this, I'll be sure to check with > you first, so we don't duplicate each others work. But I probably > won't have the time this month anyway. )-: Just to avoid double work, I'm currently implementing an Erlang LDAPv3 interface (GPL). It would be great to get some input about specific LDAP requirements related to PKI. Cheers /Tobbe From Lon.Willett@REDACTED Fri Oct 27 16:33:32 2000 From: Lon.Willett@REDACTED (Lon Willett) Date: Fri, 27 Oct 2000 15:33:32 +0100 Subject: ASN.1 questions In-Reply-To: References: <5.0.0.25.0.20001025172955.04b98c50@mail0.sse.ie> Message-ID: <5.0.0.25.0.20001027145855.00afc030@mail0.sse.ie> Hi! At 08:41 27/10/00, Torbjorn Tornkvist wrote: >Hi ! > >> Coming from the PKI world, I'll point out that the X.500/X.509 >> does require a bit more in the way of DER encoding. > >Regarding PKI, I'm a bit curious, does this also apply for LDAP ? > >I've just been reading RFC 2259 and 1778 and as I >understand it the relevant LDAP attributes should be >encoded according to the 'Undefined' syntax (RFC 1778, >chap.2.1), which states that it should be BER encoded. This stuff shouldn't be relevant for LDAP. DER (as opposed to BER) only comes into play when you need a canonical representation, e.g. in order to digitally sign some data, or to verify a digital signature. It's been a while since I read the LDAP RFCs, but IIRC they just deal with straight BER. The only catch is that the ASN.1 and X.500 directory stuff is really a mess in a few places, so its not always safe to completely decode and re-encode something. What I mean is best illustrated by an example: supposing when you store a certificate, you choose to store it in a decoded form. So the name "CN=Lon Willett,O=SSE,C=IE" from inside the cert is broken down and stored as some erlang term. Then when you try to reconstruct the certificate, you'll have to decide how to encode the string "Lon Willett". Unfortunately, you have a number of choices, although the only two good ones (for this example) would be as a PrintableString or a UTF8String. If you pick the wrong one, then the certificate signature will be invalid. Moral: one needs to be very careful in how signed pieces of data are handled. This situation is aggravated somewhat by the requirement that when verifying a signature (e.g. on a certificate), one is not allowed to assume that it (i.e. the cert) is already DER encoded, and instead one is supposed to decode it and then re-encode it using DER, and then use the re-encoded version for the signature verification. Since you are just writing an LDAP server, not a full X.500 directory (thank your lucky stars), you can probably get away with just dealing with certs and CRLs as binary values, which avoids all the encoding issues. >> If I get the time to play with this, I'll be sure to check with >> you first, so we don't duplicate each others work. But I probably >> won't have the time this month anyway. )-: > >Just to avoid double work, I'm currently implementing >an Erlang LDAPv3 interface (GPL). It would be great to >get some input about specific LDAP requirements related >to PKI. Urk. There is a big can of worms here (certificate path construction and validation). But fortunately for you, I think its all on the client side. I don't think you need to do anything special to support X.509 PKI. The only thing that I can think of that could be a problem, outside of the representation problem mentioned above, is that CRLs (certificate revocation lists) can get _very_ large, and you need to be prepared to handle attributes whose value is a CRL. Cheers, /Lon From tobbe@REDACTED Fri Oct 27 16:43:41 2000 From: tobbe@REDACTED (Torbjorn Tornkvist) Date: 27 Oct 2000 16:43:41 +0200 Subject: ASN.1 questions In-Reply-To: Lon Willett's message of "Fri, 27 Oct 2000 15:33:32 +0100" References: <5.0.0.25.0.20001025172955.04b98c50@mail0.sse.ie> <5.0.0.25.0.20001027145855.00afc030@mail0.sse.ie> Message-ID: > Since you are just writing an LDAP server, not a full > X.500 directory (thank your lucky stars), you can probably > get away with just dealing with certs and CRLs as binary > values, which avoids all the encoding issues. Hold your horses ! It is not a server I'm implementing, it is an LDAP client interface. > Urk. There is a big can of worms here (certificate path > construction and validation). But fortunately for you, > I think its all on the client side. Urk, in that case I think I'll leave that part for someone else to implement... :-) Cheers /Tobbe From ltaesch@REDACTED Sat Oct 28 02:06:59 2000 From: ltaesch@REDACTED (luc) Date: Sat, 28 Oct 2000 02:06:59 +0200 Subject: erl_interface/ ig Message-ID: <39FA18A3.EB5C6312@europemail.com> im trying to build ig, and i discovered erl_interface is not built and installed in the global make process (R7b). is that normal ? all i have in the target dir is a dir with some doc, but no include , src or lib dir, as in R6b (but i installed R6 from the rpm, not the source) this is a linux, mandrake installation. can anybody confirm on another platform that erl_interface build correctly ? -- First, they ignore you. Then, they laugh at you. Then, they fight you. Then, you win. --- Gandhi Working code is what matter, not your market capitalization.--Kurt granroth This is not a matter of waiting for something to happen. Instead, it is a matter of making something happen. -- Matthias: -------------- next part -------------- An HTML attachment was scrubbed... URL: From scott@REDACTED Sun Oct 29 06:04:52 2000 From: scott@REDACTED (Scott Lystig Fritchie) Date: Sun, 29 Oct 2000 00:04:52 -0500 Subject: Erlang in literature Message-ID: <200010290504.AAA37524@snookles.snookles.com> I found a couple of "references" in _Bridge of Birds_, by Barry Hughart. (Subtitled "A Novel of an Ancient China That Never Was".) "Behind the throne stood T'ien-kou, the Celestial Dog, whose teeth had chewed mountains in half, and beside the Celestial Dog stood Ehr-lang, who is unquestionably the greatest of all warriors because he had been able to battle the stupendous Stone Monkey to a standstill." (p. 8) Also: "On almost every corner I saw old ladies with twinkling eyes who sold soft drinks and candied fruits while they cried, 'Aiieee! Aiieee! Come closer, my children! Spread ears like elephants, and I shall tell you the tale of the great Ehr-lang, and of the time when he was devoured by the hideous Transcendent Pig!'" (p. 38) We don't get to learn anything more about Ehr-lang beyond that. One would think that the greatest of all warriors would be able to vanquish a pig, but {shrug} who can tell? I highly recommend the book to anyone who's looking for A Next Book to Read. -Scott From tgahling@REDACTED Mon Oct 30 02:40:11 2000 From: tgahling@REDACTED (Tony Gahlinger) Date: Sun, 29 Oct 2000 20:40:11 -0500 Subject: EVA simple_adaptation example fails Message-ID: <39FCD17B.F32F7216@ieee.org> The EVA simple_adaptation example of R7B will not run through on my Linux installation; it bombs out in the print-alarms function, as shown in the test run below. The problem appears to lie in the handle_call query of simple_adaptation, specifically, in the AlarmDef references: Handle = query [{A.name, A.sender, A.cause, A.severity, AlarmDef.class} || A <- table(alarm), AlarmDef <- table(alarmTable), A.name = AlarmDef.name] end, Commenting out those references lets print_alarms run to completion, though not as intended of course. A scan through handle_event functions in the same module shows remnants of previous mnemosyne bug go-arounds X = Name, % due to bug in mnemosyne... (which now do nothing as X isn't used in the functions). So, does print_alarms need fixing or is mnemosyne still buggy? --------------- test run that fails ----------------------- # erl -pa oms/ebin -oms use_snmp true -config sys Erlang (BEAM) emulator version 5.0.1 [source] Eshell V5.0.1 (abort with ^G) 1> mnesia:create_schema([node()]). ok 2> application:start(mnesia). ok 3> oms:create(). ok 4> application:start(sasl). ok 5> application:start(snmp). ok 6> application:start(oms). ok 7> board_test:init_test(). ok 8> board_test:init(). ok 9> simple_adaptation:start(). Initializing simple EVA adaptation... ok 10> board_test:remove_board(1). ok 11> ** Event: boardRemoved, 1 generated simple_adaptation:print_alarms(). Terminating simple EVA adaptation... =ERROR REPORT==== 29-Oct-2000::19:06:53 === ** gen_event handler simple_adaptation crashed. ** Was installed in alarm_handler ** Last event was: print_alarms ** When handler state == [] ** Reason == {'EXIT',mnemosyne_not_running} {error,{'EXIT',mnemosyne_not_running}} 12> --Tony ----------------------------------------------------------- Tony Gahlinger & Associates Inc. Telecommunication Consultants and Software Development 48 Combermere Crescent, Waterloo ON N2L 5B1 519-888-6267 Fax: 519-888-9127 ----------------------------------------------------------- From tgahling@REDACTED Mon Oct 30 04:21:46 2000 From: tgahling@REDACTED (Tony Gahlinger) Date: Sun, 29 Oct 2000 22:21:46 -0500 Subject: EVA simple_adaptation example fails References: <39FCD17B.F32F7216@ieee.org> Message-ID: <39FCE94A.97D65E27@ieee.org> Solved .. shook the dust off a recent archive item and found the need for application:start(mnemosyne) :( Tony Gahlinger wrote: > > The EVA simple_adaptation example of R7B will not run through on > my Linux installation; it bombs out in the print-alarms function, > as shown in the test run below. ... > simple_adaptation:print_alarms(). > Terminating simple EVA adaptation... > =ERROR REPORT==== 29-Oct-2000::19:06:53 === > ** gen_event handler simple_adaptation crashed. > ** Was installed in alarm_handler > ** Last event was: print_alarms > ** When handler state == [] > ** Reason == {'EXIT',mnemosyne_not_running} > {error,{'EXIT',mnemosyne_not_running}} > 12> --Tony ----------------------------------------------------------- Tony Gahlinger & Associates Inc. Telecommunication Consultants and Software Development 48 Combermere Crescent, Waterloo ON N2L 5B1 519-888-6267 Fax: 519-888-9127 ----------------------------------------------------------- From daniel.neri@REDACTED Mon Oct 30 11:38:27 2000 From: daniel.neri@REDACTED (Daniel Neri) Date: 30 Oct 2000 11:38:27 +0100 Subject: erl_interface/ ig In-Reply-To: luc's message of "Sat, 28 Oct 2000 02:06:59 +0200" References: <39FA18A3.EB5C6312@europemail.com> Message-ID: luc writes: > this is a linux, mandrake installation. can anybody confirm on > another platform that erl_interface build correctly ? It builds[*] and installs just fine on NetBSD. Regards, --Daniel [*] Though, as I've previously mentioned, portability.h has to be tweaked to account for the a.out object file format. But I imagine Mandrake is ELF-based, so that should not be a problem for you. -- Daniel Neri mailto:dn@REDACTED Sigicom AB, Sweden http://www.sigicom.com From raimo@REDACTED Mon Oct 30 11:57:56 2000 From: raimo@REDACTED (Raimo Niskanen) Date: Mon, 30 Oct 2000 11:57:56 +0100 Subject: erl_interface/ ig References: <39FA18A3.EB5C6312@europemail.com> Message-ID: <39FD5434.1476A32E@erix.ericsson.se> This might be the problem with erl_connect.c not compiling due to that autoconf fails to set HAVE_STRDUP correctly, which happens if strdup() is defined as a macro. Check the output from building erl_interface. If this is the case, the simplest correction would be to patch OTPROOT/lib/erl_interface/erl_connect.c: 23,28c23 < # include "config.h" /* FIXME: Autoconf Info prefers */ < #else < # define HAVE_STRDUP 1 /* we, uh, know that everyone's got strdup() */ < # ifdef VXWORKS < # undef HAVE_STRDUP /* ...'cept for VxWorks */ < # endif --- > # include "config.h" /* FIXME: Autoconf Info prefers */ 30a26,30 > # define HAVE_STRDUP 1 /* we, uh, know that everyone's got strdup() */ > #ifdef VXWORKS > # undef HAVE_STRDUP /* ...'cept for VxWorks */ > #endif > / Raimo Niskanen, Erlang/OTP luc wrote: > > im trying to build ig, and i discovered erl_interface is not built and > installed in the global make process (R7b). > is that normal ? > > all i have in the target dir is a dir with some doc, but no include , > src or lib dir, as in R6b (but i installed R6 from the rpm, not the > source) > > this is a linux, mandrake installation. > can anybody confirm on another platform that erl_interface build > correctly ? > > -- > First, they ignore you. > Then, they laugh at you. > Then, they fight you. > Then, you win. > --- Gandhi > Working code is what matter, not your market capitalization.--Kurt granroth > This is not a matter of waiting for something to happen. Instead, it is a matter of making something happen. -- Matthias: > > From bjorn@REDACTED Mon Oct 30 12:01:01 2000 From: bjorn@REDACTED (Bjorn Gustavsson) Date: 30 Oct 2000 12:01:01 +0100 Subject: Ets/dets iterators & match specs in R8 Message-ID: For the R8 release, we have PRELIMINARY plans to improve the ets/dets iterators and to add compiler/parse transform support to generate match specs automatically for the ets:select/2 function. Here are some of the extensions we are thinking of. We might do some of these, or all of them, or something entirely different if we (or someone else) come up with better ideas. 1) Allow list comprenhensions to be used on ets, dets and mnesia tables using the syntax proposed by Robert Virding. The compiler can then convert an entire list comprenhension to a call to ets:select/2, provided that all expressions in the comprehension can be expressed as a match spec. 2) Let the compiler transform the new [d]ets:fold[rl]/3 iterators to an ets:select/2, if possible. The fun must obviously be given inline for this to be possible, and it must only contain calls to guards bifs and operators and so on. 3) Introduce special syntax, for instance "match_spec (Arg...) -> ... end", to allow an match spec to be expressed in an erlang-like syntax. The match spec can be assigned to a variable, and then used in both tracing and ets:select/2. /Bj?rn -- Bj?rn Gustavsson Ericsson Utvecklings AB bjorn@REDACTED ?T2/UAB/F/P BOX 1505 +46 8 727 56 87 125 25 ?lvsj? From ltaesch@REDACTED Sun Oct 29 12:32:37 2000 From: ltaesch@REDACTED (luc) Date: Sun, 29 Oct 2000 12:32:37 +0100 Subject: erl_interface/ ig References: <39FA18A3.EB5C6312@europemail.com> <39FD5434.1476A32E@erix.ericsson.se> Message-ID: <39FC0AD4.308C3DB2@europemail.com> thks, its now installed properly. are there any way to have thes patch available on the fix section of the web ? i thought wongg put some in the spc file of its rpm, but currently, the sourceforge is still empty. maybe it would be nice to have the patch on the erlang.org site ? Raimo Niskanen wrote: > This might be the problem with erl_connect.c not compiling due to that > autoconf fails to set HAVE_STRDUP correctly, which happens if strdup() > is defined as a macro. Check the output from building erl_interface. > > If this is the case, the simplest correction would be to patch > OTPROOT/lib/erl_interface/erl_connect.c: > 23,28c23 > < # include "config.h" /* FIXME: Autoconf Info prefers > */ > < #else > < # define HAVE_STRDUP 1 /* we, uh, know that everyone's > got strdup() */ > < # ifdef VXWORKS > < # undef HAVE_STRDUP /* ...'cept for VxWorks */ > < # endif > --- > > # include "config.h" /* FIXME: Autoconf Info prefers */ > 30a26,30 > > # define HAVE_STRDUP 1 /* we, uh, know that everyone's got strdup() */ > > #ifdef VXWORKS > > # undef HAVE_STRDUP /* ...'cept for VxWorks */ > > #endif > > > > / Raimo Niskanen, Erlang/OTP > > luc wrote: > > > > im trying to build ig, and i discovered erl_interface is not built and > > installed in the global make process (R7b). > > is that normal ? > > > > all i have in the target dir is a dir with some doc, but no include , > > src or lib dir, as in R6b (but i installed R6 from the rpm, not the > > source) > > > > this is a linux, mandrake installation. > > can anybody confirm on another platform that erl_interface build > > correctly ? > > > > -- > > First, they ignore you. > > Then, they laugh at you. > > Then, they fight you. > > Then, you win. > > --- Gandhi > > Working code is what matter, not your market capitalization.--Kurt granroth > > This is not a matter of waiting for something to happen. Instead, it is a matter of making something happen. -- Matthias: > > > > -- First, they ignore you. Then, they laugh at you. Then, they fight you. Then, you win. --- Gandhi Working code is what matter, not your market capitalization.--Kurt granroth This is not a matter of waiting for something to happen. Instead, it is a matter of making something happen. -- Matthias: -------------- next part -------------- An HTML attachment was scrubbed... URL: From raimo@REDACTED Mon Oct 30 13:29:56 2000 From: raimo@REDACTED (Raimo Niskanen) Date: Mon, 30 Oct 2000 13:29:56 +0100 Subject: erl_interface/ ig References: <39FA18A3.EB5C6312@europemail.com> <39FD5434.1476A32E@erix.ericsson.se> <39FC0AD4.308C3DB2@europemail.com> Message-ID: <39FD69C4.D8023345@erix.ericsson.se> luc wrote: > > thks, its now installed properly. > > are there any way to have thes patch available on the fix section of > the web ? > > i thought wongg put some in the spc file of its rpm, but currently, > the sourceforge is still empty. > > maybe it would be nice to have the patch on the erlang.org site ? > We are working on a bunch of patches... / Raimo Niskanen, Erlang/OTP From kent@REDACTED Mon Oct 30 14:13:33 2000 From: kent@REDACTED (Kent Boortz) Date: 30 Oct 2000 14:13:33 +0100 Subject: erl_interface/ ig In-Reply-To: luc's message of "Sun, 29 Oct 2000 12:32:37 +0100" References: <39FA18A3.EB5C6312@europemail.com> <39FD5434.1476A32E@erix.ericsson.se> <39FC0AD4.308C3DB2@europemail.com> Message-ID: There will soon (in a few days) be a R7B-1 version of Erlang/OTP, i.e. a complete release. I haven't found a practical way to handle patches, some applications have changed version numbers and then directory names. We also have binary files in the distribution, BEAM files for the bootstrap compiler, that all change when recompiled. Binary files can't be part of an Unix patch (using the Unix patch program). We will think more about this. We want to make the patch handling semi-automatic, i.e. when there are important changes and the "Daily Build and Test" of the OpenSource version went well we want to automatically generate a patch. This way you can get patches more often. For us it is more easy to make a new release but I assume you don't like the idea of downloading 10 Mb for every new patch. kent From enano@REDACTED Mon Oct 30 14:20:59 2000 From: enano@REDACTED (Miguel Barreiro Paz) Date: Mon, 30 Oct 2000 14:20:59 +0100 (CET) Subject: erl_interface/ ig In-Reply-To: Message-ID: Hi, > For us it is more easy to make a new release but I assume you don't > like the idea of downloading 10 Mb for every new patch. It's what people like Sun does, anyway. Would it be practical for people to use rsync? Only differences are actually sent, and it understands binary files properly. Regards, Miguel From Jens.Peder.Terjesen@REDACTED Mon Oct 30 14:25:26 2000 From: Jens.Peder.Terjesen@REDACTED (Jens Peder Terjesen (ETO)) Date: Mon, 30 Oct 2000 14:25:26 +0100 Subject: Convert Term? Message-ID: Hi. If I have a record with nested records in it, is there an easy way to convert the nested record names without explicitly addressing each record? Example (tuple representation of records): Item = {record1,"string", {record2,{record3,field31,field32}, field22}, {record3,field31,field32}}, I want to change the name of all record3 to record4. Is this possible without explicitly traversing the whole record structure? Jens From daniel.neri@REDACTED Mon Oct 30 15:12:54 2000 From: daniel.neri@REDACTED (Daniel Neri) Date: 30 Oct 2000 15:12:54 +0100 Subject: erl_interface/ ig In-Reply-To: Kent Boortz's message of "30 Oct 2000 14:13:33 +0100" References: <39FA18A3.EB5C6312@europemail.com> <39FD5434.1476A32E@erix.ericsson.se> <39FC0AD4.308C3DB2@europemail.com> Message-ID: Kent Boortz writes: > We will think more about this. We want to make the patch handling > semi-automatic, i.e. when there are important changes and the > "Daily Build and Test" of the OpenSource version went well we want > to automatically generate a patch. This way you can get patches > more often. I know you're using Clearcase internally, but AnonCVS access to the source tree would be excellent... Regards, --Daniel -- Daniel Neri mailto:dn@REDACTED Sigicom AB, Sweden http://www.sigicom.com From jim@REDACTED Mon Oct 30 20:36:08 2000 From: jim@REDACTED (Jim Larson) Date: Mon, 30 Oct 2000 11:36:08 -0800 Subject: Ets/dets iterators & match specs in R8 In-Reply-To: Your message of "30 Oct 2000 12:01:01 +0100." Message-ID: <200010301936.LAA64174@eme110-115.Sendmail.COM> In message Bjorn writes: >For the R8 release, we have PRELIMINARY plans to improve the >ets/dets iterators and to add compiler/parse transform support >to generate match specs automatically for the ets:select/2 function. > >Here are some of the extensions we are thinking of. We might do >some of these, or all of them, or something entirely different >if we (or someone else) come up with better ideas. >... One of the nice things about ETS is that its API doesn't introduce any new language features - it *could* be implemented purely as a library on top of message-passing. I'd be suspicious of any new ETS features which would break this property. I'd prefer to see either: - an API which uses closures whenever possible to avoid creating new language features, or; - language features which are well-defined syntatctic sugar, compiling to calls which, hopefully, could be intercepted by user code. For example, allow this feature to work with any data structure which has an appropriate map function. Jim Larson From dg@REDACTED Mon Oct 30 20:16:05 2000 From: dg@REDACTED (David Gould) Date: Mon, 30 Oct 2000 11:16:05 -0800 Subject: erl_interface/ ig In-Reply-To: ; from kent@erix.ericsson.se on Mon, Oct 30, 2000 at 02:13:33PM +0100 References: <39FA18A3.EB5C6312@europemail.com> <39FD5434.1476A32E@erix.ericsson.se> <39FC0AD4.308C3DB2@europemail.com> Message-ID: <20001030111605.A17802@archimedes.oak.suse.com> On Mon, Oct 30, 2000 at 02:13:33PM +0100, Kent Boortz wrote: > > There will soon (in a few days) be a R7B-1 version of Erlang/OTP, > i.e. a complete release. I haven't found a practical way to handle > patches, some applications have changed version numbers and then > directory names. We also have binary files in the distribution, BEAM > files for the bootstrap compiler, that all change when recompiled. > Binary files can't be part of an Unix patch (using the Unix patch > program). Binaries can be handled with xdelta: ----------------------------------------------------------------- XDelta(1) XDelta(1) NAME xdelta - Invoke XDelta SYNOPSIS xdelta subcommand [ option... ] [ operand... ] DESCRIPTION XDelta provides the ability to generate deltas between a pair of files and later apply those deltas. It operates similar to the diff and patch commands, but works on binary files and does not produce a human readable output. XDelta has two subcommands, delta and patch. Delta accepts two files and produces a delta, while patch accepts one original file and a delta and produces the other file. As opposed to the the diff(1) format, which can usually be reversed, these deltas are one way. ... ----------------------------------------------------------------- -dg -- David Gould dg@REDACTED SuSE, Inc., 580 2cd St. #210, Oakland, CA 94607 510.628.3380 Any sufficiently well-rigged demo is indistinguishable from advanced technology. From ltaesch@REDACTED Mon Oct 30 10:14:03 2000 From: ltaesch@REDACTED (luc) Date: Mon, 30 Oct 2000 10:14:03 +0100 Subject: IG for c++ Message-ID: <39FD3BDB.5E8A7C6D@europemail.com> first, thanks for IG. i spent yesterday experimenting with erl_interface ang IG, and that's quite neat (after one week experimenting doing all by hand, what a progress in productivity! but , at least, i understand the problem better now). now, my problem : i d like to wrap a C++ lib. my question : how complex is it to modifiy IG to handle C++ ? this can get from simple to not so simple : - a) adding # ifdef _cplusplus , external "C" {.... at the begining and end of headers files. supposely no big deal. - b) creating wrapper C for C++ members (functions, variables) by: - extending the parsing headers to understand class:functions(arg) instead of simply functions(arg) - directly parsing the class file headers class X {.....} how complex is the grammar extension ? the parser ? is it generated with Yecc ? (the history from the doc tends to make me thinking the opposite, but there is a .y files in the sources ? thks luc -- First, they ignore you. Then, they laugh at you. Then, they fight you. Then, you win. --- Gandhi Working code is what matter, not your market capitalization.--Kurt granroth This is not a matter of waiting for something to happen. Instead, it is a matter of making something happen. -- Matthias: -------------- next part -------------- An HTML attachment was scrubbed... URL: From raimo@REDACTED Tue Oct 31 10:22:29 2000 From: raimo@REDACTED (Raimo Niskanen) Date: Tue, 31 Oct 2000 10:22:29 +0100 Subject: Do we need a letrec construct? Message-ID: <39FE8F55.B00D086F@erix.ericsson.se> I recently ran into a problem where it would have been nice to have something like the Lisp letrec construct in Erlang. Therefore I will drop a few ideas to this list to see how big interest there is for such a feature. The problem =========== My problem was that to write a generator function that reads a file and generates a lazy list/stream (i.e [Term | fun/0] or fun/0), so I started out something like this: mk_stream_from_file(Filename) -> {ok, File} = file:open(Filename), Stream = fun() -> case mk_term_from_file(File) of {ok, Term} -> [Term | Stream]; % Stream not bound eof -> [] end end end. This does unfortunately not work since the variable Stream is not bound while defining the fun, instead I had to write like this: mk_stream_from_file(Filename) -> {ok, File} = file:open(Filename), fun_stream_from_file(File). %% Create a fun/0 with environment. fun_stream_from_file(File) -> fun() -> stream_from_file(File) end. stream_from_file(File) -> case mk_term_from_file(File) of {ok, Term} -> [Term | fun_stream_from_file(File)]; eof -> [] end. This I find less clear, it pollutes the module namespace and it creates more funs in runtime, which is not for free. However, it works! The problem is to write funs that are recursive, without passing the fun as an argument to itself, and in the general case several funs might need to call each other. Suggestions =========== Here are two suggestions for extensions to Erlang. In this example I alternate between two different mk_term_from_file/1 functions: Suggestion 1, Lisp letrec style: mk_stream_from_file(Filename) -> {ok, File} = file:open(Filename), letrec Stream = fun() -> case mk_term_from_file(File) of {ok, Term} -> [Term | Stream2]; % Stream2 is actually bound! eof -> [] end end, Stream2 = fun() -> case mk_other_term_from_file(File) of {ok, Term} -> [Term | Stream1]; eof -> [] end end, end, Stream. One problem with this syntax is that Stream2 (and of course Stream) is actually bound in a source line (maybe far) above the line where it appears. I also think that 'letrec' is a lousy out-of-context choice of keyword, there must be a better alternative (but i have not given it much thought yet). Suggestion 2, new ':= 'assignment operator style: mk_stream_from_file(Filename) -> {ok, File} = file:open(Filename), {Stream, Stream2} := {fun() -> case mk_term_from_file(File) of {ok, Term} -> [Term | Stream2]; % Stream2 is actually bound! eof -> [] end end, fun() -> case mk_other_term_from_file(File) of {ok, Term} -> [Term | Stream1]; eof -> [] end end}, Stream. One problem with this syntax is what limitations to put on the left hand and right hand terms of the operator. This 'bind before definition' assignment semantics is only reasonable for funs on the right hand side and unbound variables on the left hand side, I think. An alternative for the operator might be '<-' which is used today in list comprehensions. Comments, anyone? / Raimo Niskanen, Erlang/OTP From richardc@REDACTED Tue Oct 31 12:46:59 2000 From: richardc@REDACTED (Richard Carlsson) Date: Tue, 31 Oct 2000 12:46:59 +0100 (MET) Subject: Do we need a letrec construct? In-Reply-To: <39FE8F55.B00D086F@erix.ericsson.se> Message-ID: On Tue, 31 Oct 2000, Raimo Niskanen wrote: > I recently ran into a problem where it would have been nice to have > something like the Lisp letrec construct in Erlang. > > [...] I started out something like this: > > mk_stream_from_file(Filename) -> > {ok, File} = file:open(Filename), > Stream = > fun() -> > case mk_term_from_file(File) of > {ok, Term} -> > [Term | Stream]; % Stream not bound > eof -> > [] > end > end > end. > > This does unfortunately not work since the variable Stream is not > bound while defining the fun, instead I had to write like this: > [...three functions...] > This I find less clear, it pollutes the module namespace and it > creates more funs in runtime, which is not for free. However, it > works! > > The problem is to write funs that are recursive, without passing the > fun as an argument to itself, and in the general case several funs > might need to call each other. Yes, you could write it like this - but it doesn't really scale to several mutually recursive functions (and is not so easy to read): mk_stream_from_file(Filename) -> {ok, File} = file:open(Filename), MkStream = fun (F) -> fun() -> case mk_term_from_file(File) of {ok, Term} -> [Term | F(F)]; % Stream not bound eof -> [] end end end, MkStream(MkStream). > Suggestions > =========== > > Here are two suggestions for extensions to Erlang. In this example I > alternate between two different mk_term_from_file/1 functions: > > Suggestion 1, Lisp letrec style: > > mk_stream_from_file(Filename) -> > {ok, File} = file:open(Filename), > letrec > Stream = > fun() -> > case mk_term_from_file(File) of > {ok, Term} -> > [Term | Stream2]; % Stream2 is actually bound! > eof -> > [] > end > end, > Stream2 = > fun() -> > case mk_other_term_from_file(File) of > {ok, Term} -> > [Term | Stream1]; > eof -> > [] > end > end, > end, > Stream. > > One problem with this syntax is that Stream2 (and of course Stream) is > actually bound in a source line (maybe far) above the line where it > appears. Well, that can of course be the case also when you write mutually recursive top-level functions. A more important problem is that if you allow any sort of definitions in a "letrec", you could actually define cyclic data structures: infinite(X, Y) -> letrec A = {foo, X, B}; B = {bar, Y, A} end, A. The result of "infinite(1, 2)" would then represent {foo, 1, {bar, 2, {foo, 1, {bar, 2, ...}}}}. This is something you don't want in the language for practical reasons. The solution is simply to allow only function definitions in a letrec. > I also think that 'letrec' is a lousy out-of-context choice of > keyword, there must be a better alternative (but i have not given it > much thought yet). Well, following the reasoning above, it would be a construct for defining local, possibly recursive functions. A more suitable keyword could be "define", or maybe "local". One thing, though: it should be an Erlang expression, and thus should have a value. If we allow the definitions to simply be exported from the construct, as with a "case" (and as in your example), it is not obvious what the value should be (I don't think it should be a constant). A better form would be something like define f1(X1, X2) -> ; ... fN(X1, X2, X3, X4) -> ; in end where the functions are not visible outside "in ... end". However, this would be a bit at odds with the normal name-binding mechanisms of Erlang, and I'm not sure what would be the best solution. Once upon a time, it was actually suggested that a non-recursive "let ... in ..." thing be added to Erlang, but this was turned down because of the strange effects caused by joining two different worlds of scoping rules. I think a variant of letrec could be useful in many cases where one wants local functions, not cluttering the module-level namespace, even if the problem does not involve tricky recursion like in your example (and it is not difficult to handle letrec in the compiler, as long as only function definitions are allowed). > Suggestion 2, new ':= 'assignment operator style: Same thing, just more difficult to see what is a legal definition and what is not. And a lot more difficult to read. /Richard Carlsson Richard Carlsson (richardc@REDACTED) (This space intentionally left blank.) E-mail: Richard.Carlsson@REDACTED WWW: http://www.csd.uu.se/~richardc/ From qramika@REDACTED Tue Oct 31 13:13:04 2000 From: qramika@REDACTED (Karlsson Mikael) Date: Tue, 31 Oct 2000 13:13:04 +0100 (MET) Subject: Do we need a letrec construct? Message-ID: <200010311213.NAA02910@nic.era-a.ericsson.se> I am not sure I understand the problem, but if you wan't to change the behaviour of the way of making terms, is it not possible to do something like: mk_stream_from_file(Filename) -> {ok, File} = file:open(Filename), stream(File,fun mk_term_from_file/1,first). stream(File,F,State) -> fun() -> case F(File) of {ok, Term} -> case State of first -> [Term | stream(File, fun mk_other_term_from_file/1,second)]; second -> [Term | stream(File, fun mk_term_from_file/1,first)] end; eof -> [] end end. ? / Mikael > X-Authentication-Warning: harpo.it.uu.se: richardc owned process doing -bs > Date: Tue, 31 Oct 2000 12:46:59 +0100 (MET) > From: Richard Carlsson > X-Sender: richardc@REDACTED > To: raimo.niskanen@REDACTED > cc: erlang-questions@REDACTED > Subject: Re: Do we need a letrec construct? > MIME-Version: 1.0 > > > On Tue, 31 Oct 2000, Raimo Niskanen wrote: > > > I recently ran into a problem where it would have been nice to have > > something like the Lisp letrec construct in Erlang. > > > > [...] I started out something like this: > > > > mk_stream_from_file(Filename) -> > > {ok, File} = file:open(Filename), > > Stream = > > fun() -> > > case mk_term_from_file(File) of > > {ok, Term} -> > > [Term | Stream]; % Stream not bound > > eof -> > > [] > > end > > end > > end. > > > > This does unfortunately not work since the variable Stream is not > > bound while defining the fun, instead I had to write like this: > > [...three functions...] > > This I find less clear, it pollutes the module namespace and it > > creates more funs in runtime, which is not for free. However, it > > works! > > > > The problem is to write funs that are recursive, without passing the > > fun as an argument to itself, and in the general case several funs > > might need to call each other. > > Yes, you could write it like this - but it doesn't really scale to several > mutually recursive functions (and is not so easy to read): > > mk_stream_from_file(Filename) -> > {ok, File} = file:open(Filename), > MkStream = > fun (F) -> > fun() -> > case mk_term_from_file(File) of > {ok, Term} -> > [Term | F(F)]; % Stream not bound > eof -> > [] > end > end > end, > MkStream(MkStream). > > > > Suggestions > > =========== > > > > Here are two suggestions for extensions to Erlang. In this example I > > alternate between two different mk_term_from_file/1 functions: > > > > Suggestion 1, Lisp letrec style: > > > > mk_stream_from_file(Filename) -> > > {ok, File} = file:open(Filename), > > letrec > > Stream = > > fun() -> > > case mk_term_from_file(File) of > > {ok, Term} -> > > [Term | Stream2]; % Stream2 is actually bound! > > eof -> > > [] > > end > > end, > > Stream2 = > > fun() -> > > case mk_other_term_from_file(File) of > > {ok, Term} -> > > [Term | Stream1]; > > eof -> > > [] > > end > > end, > > end, > > Stream. > > > > One problem with this syntax is that Stream2 (and of course Stream) is > > actually bound in a source line (maybe far) above the line where it > > appears. > > Well, that can of course be the case also when you write mutually > recursive top-level functions. > > A more important problem is that if you allow any sort of definitions in a > "letrec", you could actually define cyclic data structures: > > infinite(X, Y) -> > letrec > A = {foo, X, B}; > B = {bar, Y, A} > end, > A. > > The result of "infinite(1, 2)" would then represent {foo, 1, {bar, 2, > {foo, 1, {bar, 2, ...}}}}. This is something you don't want in the > language for practical reasons. > > The solution is simply to allow only function definitions in a letrec. > > > I also think that 'letrec' is a lousy out-of-context choice of > > keyword, there must be a better alternative (but i have not given it > > much thought yet). > > Well, following the reasoning above, it would be a construct for defining > local, possibly recursive functions. A more suitable keyword could be > "define", or maybe "local". > > One thing, though: it should be an Erlang expression, and thus should have > a value. If we allow the definitions to simply be exported from the > construct, as with a "case" (and as in your example), it is not obvious > what the value should be (I don't think it should be a constant). A better > form would be something like > > define > f1(X1, X2) -> ; > ... > fN(X1, X2, X3, X4) -> ; > in > > end > > where the functions are not visible outside "in ... end". However, this > would be a bit at odds with the normal name-binding mechanisms of Erlang, > and I'm not sure what would be the best solution. > > Once upon a time, it was actually suggested that a non-recursive "let ... > in ..." thing be added to Erlang, but this was turned down because of the > strange effects caused by joining two different worlds of scoping rules. > > I think a variant of letrec could be useful in many cases where one wants > local functions, not cluttering the module-level namespace, even if the > problem does not involve tricky recursion like in your example (and it is > not difficult to handle letrec in the compiler, as long as only function > definitions are allowed). > > > Suggestion 2, new ':= 'assignment operator style: > > Same thing, just more difficult to see what is a legal definition and what > is not. And a lot more difficult to read. > > /Richard Carlsson > > > > Richard Carlsson (richardc@REDACTED) (This space intentionally left blank.) > E-mail: Richard.Carlsson@REDACTED WWW: http://www.csd.uu.se/~richardc/ > > > From kent@REDACTED Tue Oct 31 17:14:18 2000 From: kent@REDACTED (Kent Boortz) Date: 31 Oct 2000 17:14:18 +0100 Subject: Patches for R7B-0 Message-ID: We still haven't decided exactly how to handle patches but feel that we have kept you waiting long enough. So we have put together some patches for changes made since R7B. The numbers beginning with "OTP-" are internal tracking numbers that we use that are not very useful to you. But keeping them makes it more easy for us to identify the changes we have made patches for. You use something like % cd otp_src_R7B-0/ % zcat patch-R7B-0-001-win-vxworks.diff.gz | patch -p1 to patch your source. Let me know if there are any problems adding the patches. kent http://www.erlang.org/download/patches/patch-R7B-0-001-win-vxworks.diff.gz Various changes made after the Unix release. http://www.erlang.org/download/patches/patch-R7B-0-002-snmp.diff.gz OTP-3704 It is now possible to register/unregister for notification of changes stored (permanently, i.e. on disk) in snmp_local_db. OTP-3725 Added direct access (read) functions to the symbolic store for faster access (accessible through the snmp module). http://www.erlang.org/download/patches/patch-R7B-0-003-erts.diff.gz OTP-3715 -nohup must work in Windows; erlsrv depends on it. http://www.erlang.org/download/patches/patch-R7B-0-004-kernel.diff.gz OTP-3714 When R6 nodes and unpatched R7 nodes are in the same network, global:register_name hangs. This is fixed with a patch to R7. Patched R7 nodes can communicate with unpatched R7 nodes OTP-3716 disk_log handles huge terms better when reading log files using chunk/2. Bugs in chunk/2 and chunk_step/3 as well as in wrap_log_reader have been fixed. OTP-3718 Kernel example uds_dist should not be read-only http://www.erlang.org/download/patches/patch-R7B-0-005-ssl.diff.gz OTP-3717 Corrected the configuration script for building with SSL. You can give the configure script the options --with-ssl use SSL (default) --with-ssl=PATH specify location of openSSL/ssleay include and lib --without-ssl don't use SSL Created from a patch contributed by Geoff Wong. http://www.erlang.org/download/patches/patch-R7B-0-006-erl_interface.diff.gz OTP-3719 The configure script did not define HAVE_STRDUP, hence the file erl_connect.c could not compile. The file erl_connect.c is now changed to not trust HAVE_STRDUP to be defined in config.h. Thanks to Mikael Pettersson and Geoff Wong. From kent@REDACTED Tue Oct 31 17:14:18 2000 From: kent@REDACTED (Kent Boortz) Date: 31 Oct 2000 17:14:18 +0100 Subject: Patches for R7B-0 Message-ID: We still haven't decided exactly how to handle patches but feel that we have kept you waiting long enough. So we have put together some patches for changes made since R7B. The numbers beginning with "OTP-" are internal tracking numbers that we use that are not very useful to you. But keeping them makes it more easy for us to identify the changes we have made patches for. You use something like % cd otp_src_R7B-0/ % zcat patch-R7B-0-001-win-vxworks.diff.gz | patch -p1 to patch your source. Let me know if there are any problems adding the patches. kent http://www.erlang.org/download/patches/patch-R7B-0-001-win-vxworks.diff.gz Various changes made after the Unix release. http://www.erlang.org/download/patches/patch-R7B-0-002-snmp.diff.gz OTP-3704 It is now possible to register/unregister for notification of changes stored (permanently, i.e. on disk) in snmp_local_db. OTP-3725 Added direct access (read) functions to the symbolic store for faster access (accessible through the snmp module). http://www.erlang.org/download/patches/patch-R7B-0-003-erts.diff.gz OTP-3715 -nohup must work in Windows; erlsrv depends on it. http://www.erlang.org/download/patches/patch-R7B-0-004-kernel.diff.gz OTP-3714 When R6 nodes and unpatched R7 nodes are in the same network, global:register_name hangs. This is fixed with a patch to R7. Patched R7 nodes can communicate with unpatched R7 nodes OTP-3716 disk_log handles huge terms better when reading log files using chunk/2. Bugs in chunk/2 and chunk_step/3 as well as in wrap_log_reader have been fixed. OTP-3718 Kernel example uds_dist should not be read-only http://www.erlang.org/download/patches/patch-R7B-0-005-ssl.diff.gz OTP-3717 Corrected the configuration script for building with SSL. You can give the configure script the options --with-ssl use SSL (default) --with-ssl=PATH specify location of openSSL/ssleay include and lib --without-ssl don't use SSL Created from a patch contributed by Geoff Wong. http://www.erlang.org/download/patches/patch-R7B-0-006-erl_interface.diff.gz OTP-3719 The configure script did not define HAVE_STRDUP, hence the file erl_connect.c could not compile. The file erl_connect.c is now changed to not trust HAVE_STRDUP to be defined in config.h. Thanks to Mikael Pettersson and Geoff Wong. From Chandrashekhar.Mullaparthi@REDACTED Tue Oct 31 18:18:25 2000 From: Chandrashekhar.Mullaparthi@REDACTED (Chandrashekhar Mullaparthi) Date: Tue, 31 Oct 2000 17:18:25 -0000 Subject: Sugg. for additional function in queue.erl Message-ID: <402DD461F109D411977E0008C791C31202A7970A@imp02mbx.one2one.co.uk> I think this function will be a small improvement to the queue module, where we want to create a queue from a list of integers, for example. new(List) when list(List) -> {List, []}. instead of writing List = [...], % some list of items Fun = fun(X, Q) -> queue:in(X, Q) end, Queue = lists:foldr(Fun, queue:new(), List) Chandru NOTICE AND DISCLAIMER: This email (including attachments) is confidential. If you have received this email in error please notify the sender immediately and delete this email from your system without copying or disseminating it or placing any reliance upon its contents. We cannot accept liability for any breaches of confidence arising through use of email. Any opinions expressed in this email (including attachments) are those of the author and do not necessarily reflect our opinions. We will not accept responsibility for any commitments made by our employees outside the scope of our business. We do not warrant the accuracy or completeness of such information. From thantos@REDACTED Tue Oct 31 21:09:23 2000 From: thantos@REDACTED (Alexander Williams) Date: Tue, 31 Oct 2000 15:09:23 -0500 Subject: Patches for R7B-0 In-Reply-To: ; from kent@erix.ericsson.se on Tue, Oct 31, 2000 at 05:14:18PM +0100 References: Message-ID: <20001031150923.A10388@gw.total-web.net> Is there any chance that there'll be a single tarchive with all the patches in a single file for we extremely lazy people, or is the full distribution of R7B-0 that's currently up that? I wasn't entirely clear on that. -- Alexander Williams (thantos@REDACTED) | In the End, "Blue Jester needs food." | Oblivion "Blue Jester needs fuku-wearing cuties." | Always http://www.chancel.org | Wins From kent@REDACTED Tue Oct 31 22:56:05 2000 From: kent@REDACTED (Kent Boortz) Date: 31 Oct 2000 22:56:05 +0100 Subject: Patches for R7B-0 In-Reply-To: Alexander Williams's message of "Tue, 31 Oct 2000 15:09:23 -0500" References: <20001031150923.A10388@gw.total-web.net> Message-ID: > Is there any chance that there'll be a single tarchive with all the > patches in a single file for we extremely lazy people, or is the full > distribution of R7B-0 that's currently up that? I wasn't entirely > clear on that. No, you have to add the individual patches to your unpacked "otp_src_R7B-0.tar.gz". We are investigating how to create patches automatically - to get you patches sooner and distributed in convenient way for you, kent From taavi@REDACTED Tue Oct 31 23:39:14 2000 From: taavi@REDACTED (Taavi Talvik) Date: Wed, 1 Nov 2000 00:39:14 +0200 (EET) Subject: Patches for R7B-0 In-Reply-To: Message-ID: On 31 Oct 2000, Kent Boortz wrote: > No, you have to add the individual patches to your unpacked > "otp_src_R7B-0.tar.gz". > > We are investigating how to create patches automatically - to get you > patches sooner and distributed in convenient way for you, I do not know Ericsson policy towards Erlang development, but cvs repository (read-only) would be nice. It opens lot of possibilities for distribution (cvs, cvsup, cvsweb). It will probably also accelerate development of Erlang as a language and surrounding libraries. best regards, taavi